From dc2f2f47a893dd4a4ac465a3ed5fdceab2e000a0 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Wed, 29 Jun 2022 22:51:23 +0200 Subject: [PATCH 1/3] rcreate: remove legacy encryption modes for new repos, fixes #6490 These are legacy crypto modes based on AES-CTR mode: (repokey|keyfile)[-blake2] New crypto modes with session keys and AEAD ciphers: (repokey|keyfile)[-blake2]-(aes-ocb|chacha20-poly1305) Tests needed some changes: - most used repokey/keyfile, changed to new modes - some nonce tests removed, the new crypto code does not generate the repo side nonces any more (were only used for AES-CTR) --- src/borg/archiver.py | 40 +-- src/borg/crypto/key.py | 21 +- src/borg/testsuite/archiver.py | 461 ++++++++++++++------------------- 3 files changed, 228 insertions(+), 294 deletions(-) diff --git a/src/borg/archiver.py b/src/borg/archiver.py index 8f7c4e449b..1bb4c14873 100644 --- a/src/borg/archiver.py +++ b/src/borg/archiver.py @@ -48,6 +48,8 @@ from .compress import CompressionSpec, ZLIB, ZLIB_legacy, ObfuscateSize from .crypto.key import key_creator, key_argument_names, tam_required_file, tam_required from .crypto.key import RepoKey, KeyfileKey, Blake2RepoKey, Blake2KeyfileKey, FlexiKey + from .crypto.key import AESOCBRepoKey, CHPORepoKey, Blake2AESOCBRepoKey, Blake2CHPORepoKey + from .crypto.key import AESOCBKeyfileKey, CHPOKeyfileKey, Blake2AESOCBKeyfileKey, Blake2CHPOKeyfileKey from .crypto.keymanager import KeyManager from .helpers import EXIT_SUCCESS, EXIT_WARNING, EXIT_ERROR, EXIT_SIGNAL_BASE from .helpers import Error, NoManifestError, set_ec @@ -503,28 +505,32 @@ def do_change_location(self, args, repository, manifest, key, cache): return EXIT_ERROR if args.key_mode == 'keyfile': - if isinstance(key, RepoKey): - key_new = KeyfileKey(repository) - elif isinstance(key, Blake2RepoKey): - key_new = Blake2KeyfileKey(repository) - elif isinstance(key, (KeyfileKey, Blake2KeyfileKey)): - print(f"Location already is {args.key_mode}") - return EXIT_SUCCESS + if isinstance(key, AESOCBRepoKey): + key_new = AESOCBKeyfileKey(repository) + elif isinstance(key, CHPORepoKey): + key_new = CHPOKeyfileKey(repository) + elif isinstance(key, Blake2AESOCBRepoKey): + key_new = Blake2AESOCBKeyfileKey(repository) + elif isinstance(key, Blake2CHPORepoKey): + key_new = Blake2CHPOKeyfileKey(repository) else: - raise Error("Unsupported key type") + print("Change not needed or not supported.") + return EXIT_WARNING if args.key_mode == 'repokey': - if isinstance(key, KeyfileKey): - key_new = RepoKey(repository) - elif isinstance(key, Blake2KeyfileKey): - key_new = Blake2RepoKey(repository) - elif isinstance(key, (RepoKey, Blake2RepoKey)): - print(f"Location already is {args.key_mode}") - return EXIT_SUCCESS + if isinstance(key, AESOCBKeyfileKey): + key_new = AESOCBRepoKey(repository) + elif isinstance(key, CHPOKeyfileKey): + key_new = CHPORepoKey(repository) + elif isinstance(key, Blake2AESOCBKeyfileKey): + key_new = Blake2AESOCBRepoKey(repository) + elif isinstance(key, Blake2CHPOKeyfileKey): + key_new = Blake2CHPORepoKey(repository) else: - raise Error("Unsupported key type") + print("Change not needed or not supported.") + return EXIT_WARNING for name in ('repository_id', 'enc_key', 'enc_hmac_key', 'id_key', 'chunk_seed', - 'tam_required', 'nonce_manager', 'cipher'): + 'tam_required', 'sessionid', 'cipher'): value = getattr(key, name) setattr(key_new, name, value) diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index 7dfda58511..e1c58c47af 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -98,7 +98,7 @@ def identify_key(manifest_data): if key_type == KeyType.PASSPHRASE: # legacy, see comment in KeyType class. return RepoKey - for key in AVAILABLE_KEY_TYPES: + for key in LEGACY_KEY_TYPES + AVAILABLE_KEY_TYPES: if key.TYPE == key_type: return key else: @@ -977,7 +977,7 @@ class CHPORepoKey(ID_HMAC_SHA_256, AEADKeyBase, FlexiKey): class Blake2AESOCBKeyfileKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): TYPES_ACCEPTABLE = {KeyType.BLAKE2AESOCBKEYFILE, KeyType.BLAKE2AESOCBREPO} TYPE = KeyType.BLAKE2AESOCBKEYFILE - NAME = 'key file Blake2b AES-OCB' + NAME = 'key file BLAKE2b AES-OCB' ARG_NAME = 'keyfile-blake2-aes-ocb' STORAGE = KeyBlobStorage.KEYFILE CIPHERSUITE = AES256_OCB @@ -986,7 +986,7 @@ class Blake2AESOCBKeyfileKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): class Blake2AESOCBRepoKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): TYPES_ACCEPTABLE = {KeyType.BLAKE2AESOCBKEYFILE, KeyType.BLAKE2AESOCBREPO} TYPE = KeyType.BLAKE2AESOCBREPO - NAME = 'repokey Blake2b AES-OCB' + NAME = 'repokey BLAKE2b AES-OCB' ARG_NAME = 'repokey-blake2-aes-ocb' STORAGE = KeyBlobStorage.REPO CIPHERSUITE = AES256_OCB @@ -995,7 +995,7 @@ class Blake2AESOCBRepoKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): class Blake2CHPOKeyfileKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): TYPES_ACCEPTABLE = {KeyType.BLAKE2CHPOKEYFILE, KeyType.BLAKE2CHPOREPO} TYPE = KeyType.BLAKE2CHPOKEYFILE - NAME = 'key file Blake2b ChaCha20-Poly1305' + NAME = 'key file BLAKE2b ChaCha20-Poly1305' ARG_NAME = 'keyfile-blake2-chacha20-poly1305' STORAGE = KeyBlobStorage.KEYFILE CIPHERSUITE = CHACHA20_POLY1305 @@ -1004,16 +1004,23 @@ class Blake2CHPOKeyfileKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): class Blake2CHPORepoKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): TYPES_ACCEPTABLE = {KeyType.BLAKE2CHPOKEYFILE, KeyType.BLAKE2CHPOREPO} TYPE = KeyType.BLAKE2CHPOREPO - NAME = 'repokey Blake2b ChaCha20-Poly1305' + NAME = 'repokey BLAKE2b ChaCha20-Poly1305' ARG_NAME = 'repokey-blake2-chacha20-poly1305' STORAGE = KeyBlobStorage.REPO CIPHERSUITE = CHACHA20_POLY1305 +LEGACY_KEY_TYPES = ( + # legacy (AES-CTR based) crypto + KeyfileKey, RepoKey, + Blake2KeyfileKey, Blake2RepoKey, +) + AVAILABLE_KEY_TYPES = ( + # these are available encryption modes for new repositories + # not encrypted modes PlaintextKey, - KeyfileKey, RepoKey, AuthenticatedKey, - Blake2KeyfileKey, Blake2RepoKey, Blake2AuthenticatedKey, + AuthenticatedKey, Blake2AuthenticatedKey, # new crypto AESOCBKeyfileKey, AESOCBRepoKey, CHPOKeyfileKey, CHPORepoKey, diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index f7b11206a4..7c34da2380 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -36,7 +36,7 @@ from ..chunker import has_seek_hole from ..constants import * # NOQA from ..crypto.low_level import bytes_to_long, num_cipher_blocks -from ..crypto.key import FlexiKey, RepoKey, KeyfileKey, Passphrase, TAMRequiredError +from ..crypto.key import FlexiKey, AESOCBRepoKey, AESOCBKeyfileKey, CHPOKeyfileKey, Passphrase, TAMRequiredError from ..crypto.keymanager import RepoIdMismatch, NotABorgKeyFile from ..crypto.file_integrity import FileIntegrityError from ..helpers import Location, get_security_dir @@ -59,6 +59,8 @@ from .platform import fakeroot_detected, is_darwin from . import key +RK_ENCRYPTION = '--encryption=repokey-aes-ocb' +KF_ENCRYPTION = '--encryption=keyfile-chacha20-poly1305' src_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) @@ -391,7 +393,7 @@ def get_security_dir(self): def test_basic_functionality(self): have_root = self.create_test_files() # fork required to test show-rc output - output = self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey', '--show-version', '--show-rc', fork=True) + output = self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION, '--show-version', '--show-rc', fork=True) self.assert_in('borgbackup version', output) self.assert_in('terminating with success status, rc 0', output) self.cmd(f'--repo={self.repository_location}', 'create', '--exclude-nodump', 'test', 'input') @@ -481,7 +483,7 @@ def test_init_parent_dirs(self): assert os.path.exists(parent_path) def test_unix_socket(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) try: sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.bind(os.path.join(self.input_path, 'unix-socket')) @@ -499,7 +501,7 @@ def test_unix_socket(self): @pytest.mark.skipif(not are_symlinks_supported(), reason='symlinks not supported') def test_symlink_extract(self): self.create_test_files() - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') with changedir('output'): self.cmd(f'--repo={self.repository_location}', 'extract', 'test') @@ -512,7 +514,7 @@ def test_hardlinked_symlinks_extract(self): with changedir('input'): os.symlink('target', 'symlink1') os.link('symlink1', 'symlink2', follow_symlinks=False) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') with changedir('output'): output = self.cmd(f'--repo={self.repository_location}', 'extract', 'test') @@ -546,7 +548,7 @@ def has_noatime(some_file): atime, mtime = 123456780, 234567890 have_noatime = has_noatime('input/file1') os.utime('input/file1', (atime, mtime)) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', '--atime', 'test', 'input') with changedir('output'): self.cmd(f'--repo={self.repository_location}', 'extract', 'test') @@ -566,7 +568,7 @@ def test_birthtime(self): birthtime, mtime, atime = 946598400, 946684800, 946771200 os.utime('input/file1', (atime, birthtime)) os.utime('input/file1', (atime, mtime)) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') with changedir('output'): self.cmd(f'--repo={self.repository_location}', 'extract', 'test') @@ -582,7 +584,7 @@ def test_nobirthtime(self): birthtime, mtime, atime = 946598400, 946684800, 946771200 os.utime('input/file1', (atime, birthtime)) os.utime('input/file1', (atime, mtime)) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input', '--nobirthtime') with changedir('output'): self.cmd(f'--repo={self.repository_location}', 'extract', 'test') @@ -643,7 +645,7 @@ def is_sparse(fn, total_size, hole_size): if sparse_support: # we could create a sparse input file, so creating a backup of it and # extracting it again (as sparse) should also work: - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') with changedir(self.output_path): self.cmd(f'--repo={self.repository_location}', 'extract', 'test', '--sparse') @@ -662,7 +664,7 @@ def test_unusual_filenames(self): filename = os.path.join(self.input_path, filename) with open(filename, 'wb'): pass - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') for filename in filenames: with changedir('output'): @@ -672,7 +674,7 @@ def test_unusual_filenames(self): def test_repository_swap_detection(self): self.create_test_files() os.environ['BORG_PASSPHRASE'] = 'passphrase' - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) repository_id = self._extract_repository_id(self.repository_path) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') shutil.rmtree(self.repository_path) @@ -689,7 +691,7 @@ def test_repository_swap_detection2(self): self.create_test_files() self.cmd(f'--repo={self.repository_location}_unencrypted', 'rcreate', '--encryption=none') os.environ['BORG_PASSPHRASE'] = 'passphrase' - self.cmd(f'--repo={self.repository_location}_encrypted', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}_encrypted', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}_encrypted', 'create', 'test', 'input') shutil.rmtree(self.repository_path + '_encrypted') os.rename(self.repository_path + '_unencrypted', self.repository_path + '_encrypted') @@ -702,7 +704,7 @@ def test_repository_swap_detection2(self): def test_repository_swap_detection_no_cache(self): self.create_test_files() os.environ['BORG_PASSPHRASE'] = 'passphrase' - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) repository_id = self._extract_repository_id(self.repository_path) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') shutil.rmtree(self.repository_path) @@ -720,7 +722,7 @@ def test_repository_swap_detection2_no_cache(self): self.create_test_files() self.cmd(f'--repo={self.repository_location}_unencrypted', 'rcreate', '--encryption=none') os.environ['BORG_PASSPHRASE'] = 'passphrase' - self.cmd(f'--repo={self.repository_location}_encrypted', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}_encrypted', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}_encrypted', 'create', 'test', 'input') self.cmd(f'--repo={self.repository_location}_unencrypted', 'rdelete', '--cache-only') self.cmd(f'--repo={self.repository_location}_encrypted', 'rdelete', '--cache-only') @@ -736,12 +738,12 @@ def test_repository_swap_detection_repokey_blank_passphrase(self): # Check that a repokey repo with a blank passphrase is considered like a plaintext repo. self.create_test_files() # User initializes her repository with her passphrase - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') # Attacker replaces it with her own repository, which is encrypted but has no passphrase set shutil.rmtree(self.repository_path) with environment_variable(BORG_PASSPHRASE=''): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) # Delete cache & security database, AKA switch to user perspective self.cmd(f'--repo={self.repository_location}', 'rdelete', '--cache-only') shutil.rmtree(self.get_security_dir()) @@ -756,7 +758,7 @@ def test_repository_swap_detection_repokey_blank_passphrase(self): self.cmd(f'--repo={self.repository_location}', 'create', 'test.2', 'input') def test_repository_move(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) security_dir = self.get_security_dir() os.rename(self.repository_path, self.repository_path + '_new') with environment_variable(BORG_RELOCATED_REPO_ACCESS_IS_OK='yes'): @@ -774,7 +776,7 @@ def test_repository_move(self): assert os.path.exists(os.path.join(security_dir, file)) def test_security_dir_compat(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) with open(os.path.join(self.get_security_dir(), 'location'), 'w') as fd: fd.write('something outdated') # This is fine, because the cache still has the correct information. security_dir and cache can disagree @@ -802,7 +804,7 @@ def test_unknown_unencrypted(self): self.cmd(f'--repo={self.repository_location}', 'rinfo') def test_strip_components(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('dir/file') self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') with changedir('output'): @@ -831,7 +833,7 @@ def _extract_hardlinks_setup(self): os.link(os.path.join(self.input_path, 'dir1/source2'), os.path.join(self.input_path, 'dir1/aaaa')) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') @requires_hardlinks @@ -921,7 +923,7 @@ def test_extract_hardlinks_twice(self): assert os.stat('input/b/hardlink').st_nlink == 2 def test_extract_include_exclude(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('file2', size=1024 * 80) self.create_regular_file('file3', size=1024 * 80) @@ -938,7 +940,7 @@ def test_extract_include_exclude(self): self.assert_equal(sorted(os.listdir('output/input')), ['file1', 'file3']) def test_extract_include_exclude_regex(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('file2', size=1024 * 80) self.create_regular_file('file3', size=1024 * 80) @@ -971,7 +973,7 @@ def test_extract_include_exclude_regex(self): self.assert_equal(sorted(os.listdir('output/input')), ['file3']) def test_extract_include_exclude_regex_from_file(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('file2', size=1024 * 80) self.create_regular_file('file3', size=1024 * 80) @@ -1011,7 +1013,7 @@ def test_extract_include_exclude_regex_from_file(self): self.assert_equal(sorted(os.listdir('output/input')), ['file3']) def test_extract_with_pattern(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file("file1", size=1024 * 80) self.create_regular_file("file2", size=1024 * 80) self.create_regular_file("file3", size=1024 * 80) @@ -1044,7 +1046,7 @@ def test_extract_with_pattern(self): self.assert_equal(sorted(os.listdir("output/input")), ["file1", "file2", "file333"]) def test_extract_list_output(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file', size=1024 * 80) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') @@ -1069,7 +1071,7 @@ def test_extract_list_output(self): self.assert_in("input/file", output) def test_extract_progress(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file', size=1024 * 80) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') @@ -1078,7 +1080,7 @@ def test_extract_progress(self): assert 'Extracting:' in output def _create_test_caches(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('cache1/%s' % CACHE_TAG_NAME, contents=CACHE_TAG_CONTENTS + b' extra stuff') @@ -1092,7 +1094,7 @@ def _create_test_caches(self): contents=CACHE_TAG_CONTENTS + b' extra stuff') def test_create_stdin(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) input_data = b'\x00foo\n\nbar\n \n' self.cmd(f'--repo={self.repository_location}', 'create', 'test', '-', input=input_data) item = json.loads(self.cmd(f'--repo={self.repository_location}', 'list', 'test', '--json-lines')) @@ -1104,7 +1106,7 @@ def test_create_stdin(self): assert extracted_data == input_data def test_create_content_from_command(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) input_data = 'some test content' name = 'a/b/c' self.cmd(f'--repo={self.repository_location}', 'create', '--stdin-name', name, '--content-from-command', @@ -1118,7 +1120,7 @@ def test_create_content_from_command(self): assert extracted_data == input_data + '\n' def test_create_content_from_command_with_failed_command(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', '--content-from-command', 'test', '--', 'sh', '-c', 'exit 73;', exit_code=2) assert output.endswith("Command 'sh' exited with status 73\n") @@ -1126,12 +1128,12 @@ def test_create_content_from_command_with_failed_command(self): assert archive_list['archives'] == [] def test_create_content_from_command_missing_command(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', 'test', '--content-from-command', exit_code=2) assert output.endswith('No command given.\n') def test_create_paths_from_stdin(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file("file1", size=1024 * 80) self.create_regular_file("dir1/file2", size=1024 * 80) self.create_regular_file("dir1/file3", size=1024 * 80) @@ -1145,7 +1147,7 @@ def test_create_paths_from_stdin(self): assert paths == ['input/file1', 'input/dir1', 'input/file4'] def test_create_paths_from_command(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file("file1", size=1024 * 80) self.create_regular_file("file2", size=1024 * 80) self.create_regular_file("file3", size=1024 * 80) @@ -1159,7 +1161,7 @@ def test_create_paths_from_command(self): assert paths == ['input/file1', 'input/file2', 'input/file3'] def test_create_paths_from_command_with_failed_command(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', '--paths-from-command', 'test', '--', 'sh', '-c', 'exit 73;', exit_code=2) assert output.endswith("Command 'sh' exited with status 73\n") @@ -1167,18 +1169,18 @@ def test_create_paths_from_command_with_failed_command(self): assert archive_list['archives'] == [] def test_create_paths_from_command_missing_command(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', 'test', '--paths-from-command', exit_code=2) assert output.endswith('No command given.\n') def test_create_without_root(self): """test create without a root""" - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', exit_code=2) def test_create_pattern_root(self): """test create with only a root pattern""" - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('file2', size=1024 * 80) output = self.cmd(f'--repo={self.repository_location}', 'create', 'test', '-v', '--list', '--pattern=R input') @@ -1187,7 +1189,7 @@ def test_create_pattern_root(self): def test_create_pattern(self): """test file patterns during create""" - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('file2', size=1024 * 80) self.create_regular_file('file_important', size=1024 * 80) @@ -1200,7 +1202,7 @@ def test_create_pattern(self): def test_create_pattern_file(self): """test file patterns during create""" - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('file2', size=1024 * 80) self.create_regular_file('otherfile', size=1024 * 80) @@ -1219,7 +1221,7 @@ def test_create_pattern_exclude_folder_but_recurse(self): with open(self.patterns_file_path2, 'wb') as fd: fd.write(b'+ input/x/b\n- input/x*\n') - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('x/a/foo_a', size=1024 * 80) self.create_regular_file('x/b/foo_b', size=1024 * 80) self.create_regular_file('y/foo_y', size=1024 * 80) @@ -1236,7 +1238,7 @@ def test_create_pattern_exclude_folder_no_recurse(self): with open(self.patterns_file_path2, 'wb') as fd: fd.write(b'+ input/x/b\n! input/x*\n') - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('x/a/foo_a', size=1024 * 80) self.create_regular_file('x/b/foo_b', size=1024 * 80) self.create_regular_file('y/foo_y', size=1024 * 80) @@ -1253,7 +1255,7 @@ def test_create_pattern_intermediate_folders_first(self): with open(self.patterns_file_path2, 'wb') as fd: fd.write(b'+ input/x/a\n+ input/x/b\n- input/x*\n') - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('x/a/foo_a', size=1024 * 80) self.create_regular_file('x/b/foo_b', size=1024 * 80) @@ -1274,7 +1276,7 @@ def test_create_pattern_intermediate_folders_first(self): def test_create_no_cache_sync(self): self.create_test_files() - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'rdelete', '--cache-only') create_json = json.loads(self.cmd(f'--repo={self.repository_location}', 'create', '--no-cache-sync', '--json', '--error', @@ -1289,7 +1291,7 @@ def test_create_no_cache_sync(self): self.cmd(f'--repo={self.repository_location}', 'check') def test_extract_pattern_opt(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('file2', size=1024 * 80) self.create_regular_file('file_important', size=1024 * 80) @@ -1317,7 +1319,7 @@ def test_recreate_exclude_caches(self): self._assert_test_caches() def _create_test_tagged(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('tagged1/.NOBACKUP') self.create_regular_file('tagged2/00-NOBACKUP') @@ -1342,7 +1344,7 @@ def test_recreate_exclude_tagged(self): self._assert_test_tagged() def _create_test_keep_tagged(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file0', size=1024) self.create_regular_file('tagged1/.NOBACKUP1') self.create_regular_file('tagged1/file1', size=1024) @@ -1408,7 +1410,7 @@ def patched_fchown(fd, uid, gid): capabilities = b'\x01\x00\x00\x02\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' self.create_regular_file('file') xattr.setxattr(b'input/file', b'security.capability', capabilities) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') with changedir('output'): with patch.object(os, 'fchown', patched_fchown): @@ -1447,7 +1449,7 @@ def patched_setxattr_EACCES(*args, **kwargs): assert os.path.isfile(input_abspath) def test_path_normalization(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('dir1/dir2/file', size=1024 * 80) with changedir('input/dir1/dir2'): self.cmd(f'--repo={self.repository_location}', 'create', 'test', @@ -1457,7 +1459,7 @@ def test_path_normalization(self): self.assert_in(' input/dir1/dir2/file', output) def test_exclude_normalization(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('file2', size=1024 * 80) with changedir('input'): @@ -1477,13 +1479,13 @@ def test_exclude_normalization(self): def test_repeated_files(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input', 'input') def test_overwrite(self): self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('dir2/file2', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') # Overwriting regular files and directories should be supported os.mkdir('output/input') @@ -1502,7 +1504,7 @@ def test_overwrite(self): def test_rename(self): self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('dir2/file2', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') self.cmd(f'--repo={self.repository_location}', 'create', 'test.2', 'input') self.cmd(f'--repo={self.repository_location}', 'extract', 'test', '--dry-run') @@ -1521,7 +1523,7 @@ def test_rename(self): def test_info(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') info_repo = self.cmd(f'--repo={self.repository_location}', 'rinfo') assert 'Original size:' in info_repo @@ -1532,14 +1534,14 @@ def test_info(self): def test_info_json(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') info_repo = json.loads(self.cmd(f'--repo={self.repository_location}', 'rinfo', '--json')) repository = info_repo['repository'] assert len(repository['id']) == 64 assert 'last_modified' in repository assert datetime.strptime(repository['last_modified'], ISO_FORMAT) # must not raise - assert info_repo['encryption']['mode'] == 'repokey' + assert info_repo['encryption']['mode'] == RK_ENCRYPTION[13:] assert 'keyfile' not in info_repo['encryption'] cache = info_repo['cache'] stats = cache['stats'] @@ -1562,7 +1564,7 @@ def test_info_json(self): def test_info_json_of_empty_archive(self): """See https://github.com/borgbackup/borg/issues/6120""" - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) info_repo = json.loads(self.cmd(f'--repo={self.repository_location}', 'info', '--json', '--first=1')) assert info_repo["archives"] == [] info_repo = json.loads(self.cmd(f'--repo={self.repository_location}', 'info', '--json', '--last=1')) @@ -1570,7 +1572,7 @@ def test_info_json_of_empty_archive(self): def test_comment(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test1', 'input') self.cmd(f'--repo={self.repository_location}', 'create', 'test2', 'input', '--comment', 'this is the comment') self.cmd(f'--repo={self.repository_location}', 'create', 'test3', 'input', '--comment', '"deleted" comment') @@ -1590,7 +1592,7 @@ def test_comment(self): def test_delete(self): self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('dir2/file2', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') self.cmd(f'--repo={self.repository_location}', 'create', 'test.2', 'input') self.cmd(f'--repo={self.repository_location}', 'create', 'test.3', 'input') @@ -1610,7 +1612,7 @@ def test_delete(self): def test_delete_multiple(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test1', 'input') self.cmd(f'--repo={self.repository_location}', 'create', 'test2', 'input') self.cmd(f'--repo={self.repository_location}', 'create', 'test3', 'input') @@ -1623,7 +1625,7 @@ def test_delete_multiple(self): def test_delete_repo(self): self.create_regular_file('file1', size=1024 * 80) self.create_regular_file('dir2/file2', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') self.cmd(f'--repo={self.repository_location}', 'create', 'test.2', 'input') os.environ['BORG_DELETE_I_KNOW_WHAT_I_AM_DOING'] = 'no' @@ -1668,7 +1670,7 @@ def test_delete_double_force(self): self.assert_not_in('test', output) def test_corrupted_repository(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('test') self.cmd(f'--repo={self.repository_location}', 'extract', 'test', '--dry-run') output = self.cmd(f'--repo={self.repository_location}', 'check', '--show-version') @@ -1683,7 +1685,7 @@ def test_corrupted_repository(self): self.assert_in('Starting repository check', output) # --info given for root logger def test_readonly_check(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('test') with self.read_only(self.repository_path): # verify that command normally doesn't work with read-only repo @@ -1698,7 +1700,7 @@ def test_readonly_check(self): self.cmd(f'--repo={self.repository_location}', 'check', '--verify-data', '--bypass-lock') def test_readonly_diff(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('a') self.create_src_archive('b') with self.read_only(self.repository_path): @@ -1714,7 +1716,7 @@ def test_readonly_diff(self): self.cmd(f'--repo={self.repository_location}', 'diff', 'a', 'b', '--bypass-lock') def test_readonly_export_tar(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('test') with self.read_only(self.repository_path): # verify that command normally doesn't work with read-only repo @@ -1729,7 +1731,7 @@ def test_readonly_export_tar(self): self.cmd(f'--repo={self.repository_location}', 'export-tar', 'test', 'test.tar', '--bypass-lock') def test_readonly_extract(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('test') with self.read_only(self.repository_path): # verify that command normally doesn't work with read-only repo @@ -1744,7 +1746,7 @@ def test_readonly_extract(self): self.cmd(f'--repo={self.repository_location}', 'extract', 'test', '--bypass-lock') def test_readonly_info(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('test') with self.read_only(self.repository_path): # verify that command normally doesn't work with read-only repo @@ -1759,7 +1761,7 @@ def test_readonly_info(self): self.cmd(f'--repo={self.repository_location}', 'rinfo', '--bypass-lock') def test_readonly_list(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('test') with self.read_only(self.repository_path): # verify that command normally doesn't work with read-only repo @@ -1775,7 +1777,7 @@ def test_readonly_list(self): @unittest.skipUnless(llfuse, 'llfuse not installed') def test_readonly_mount(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('test') with self.read_only(self.repository_path): # verify that command normally doesn't work with read-only repo @@ -1796,13 +1798,13 @@ def test_readonly_mount(self): @pytest.mark.skipif('BORG_TESTS_IGNORE_MODES' in os.environ, reason='modes unreliable') def test_umask(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') mode = os.stat(self.repository_path).st_mode self.assertEqual(stat.S_IMODE(mode), 0o700) def test_create_dry_run(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', '--dry-run', 'test', 'input') # Make sure no archive has been created with Repository(self.repository_path) as repository: @@ -1825,23 +1827,23 @@ def cmd_raises_unknown_feature(self, args): assert excinfo.value.args == (['unknown-feature'],) def test_unknown_feature_on_create(self): - print(self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey')) + print(self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION)) self.add_unknown_feature(Manifest.Operation.WRITE) self.cmd_raises_unknown_feature([f'--repo={self.repository_location}', 'create', 'test', 'input']) def test_unknown_feature_on_cache_sync(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'rdelete', '--cache-only') self.add_unknown_feature(Manifest.Operation.READ) self.cmd_raises_unknown_feature([f'--repo={self.repository_location}', 'create', 'test', 'input']) def test_unknown_feature_on_change_passphrase(self): - print(self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey')) + print(self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION)) self.add_unknown_feature(Manifest.Operation.CHECK) self.cmd_raises_unknown_feature([f'--repo={self.repository_location}', 'key', 'change-passphrase']) def test_unknown_feature_on_read(self): - print(self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey')) + print(self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION)) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') self.add_unknown_feature(Manifest.Operation.READ) with changedir('output'): @@ -1851,13 +1853,13 @@ def test_unknown_feature_on_read(self): self.cmd_raises_unknown_feature([f'--repo={self.repository_location}', 'info', '-a', 'test']) def test_unknown_feature_on_rename(self): - print(self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey')) + print(self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION)) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') self.add_unknown_feature(Manifest.Operation.CHECK) self.cmd_raises_unknown_feature([f'--repo={self.repository_location}', 'rename', 'test', 'other']) def test_unknown_feature_on_delete(self): - print(self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey')) + print(self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION)) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') self.add_unknown_feature(Manifest.Operation.DELETE) # delete of an archive raises @@ -1868,7 +1870,7 @@ def test_unknown_feature_on_delete(self): @unittest.skipUnless(llfuse, 'llfuse not installed') def test_unknown_feature_on_mount(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') self.add_unknown_feature(Manifest.Operation.READ) mountpoint = os.path.join(self.tmpdir, 'mountpoint') @@ -1883,7 +1885,7 @@ def test_unknown_mandatory_feature_in_cache(self): else: path_prefix = '' - print(self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey')) + print(self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION)) with Repository(self.repository_path, exclusive=True) as repository: if path_prefix: @@ -1919,13 +1921,13 @@ def wipe_wrapper(*args): def test_progress_on(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', 'test4', 'input', '--progress') self.assert_in("\r", output) def test_progress_off(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', 'test5', 'input') self.assert_not_in("\r", output) @@ -1936,7 +1938,7 @@ def test_file_status(self): self.create_regular_file('file1', size=1024 * 80) time.sleep(1) # file2 must have newer timestamps than file1 self.create_regular_file('file2', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', '--list', 'test', 'input') self.assert_in("A input/file1", output) self.assert_in("A input/file2", output) @@ -1952,7 +1954,7 @@ def test_file_status_cs_cache_mode(self): self.create_regular_file('file1', contents=b'123') time.sleep(1) # file2 must have newer timestamps than file1 self.create_regular_file('file2', size=10) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', 'test1', 'input', '--list', '--files-cache=ctime,size') # modify file1, but cheat with the mtime (and atime) and also keep same size: @@ -1969,7 +1971,7 @@ def test_file_status_ms_cache_mode(self): self.create_regular_file('file1', size=10) time.sleep(1) # file2 must have newer timestamps than file1 self.create_regular_file('file2', size=10) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', '--list', '--files-cache=mtime,size', 'test1', 'input') # change mode of file1, no content change: @@ -1985,7 +1987,7 @@ def test_file_status_rc_cache_mode(self): self.create_regular_file('file1', size=10) time.sleep(1) # file2 must have newer timestamps than file1 self.create_regular_file('file2', size=10) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', '--list', '--files-cache=rechunk,ctime', 'test1', 'input') # no changes here, but this mode rechunks unconditionally @@ -2002,7 +2004,7 @@ def test_file_status_excluded(self): if has_lchflags: self.create_regular_file('file3', size=1024 * 80) platform.set_flags(os.path.join(self.input_path, 'file3'), stat.UF_NODUMP) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'create', '--list', '--exclude-nodump', 'test', 'input') self.assert_in("A input/file1", output) self.assert_in("A input/file2", output) @@ -2018,7 +2020,7 @@ def test_file_status_excluded(self): def test_create_json(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) create_info = json.loads(self.cmd(f'--repo={self.repository_location}', 'create', '--json', 'test', 'input')) # The usual keys @@ -2038,7 +2040,7 @@ def test_create_topical(self): self.create_regular_file('file1', size=1024 * 80) time.sleep(1) # file2 must have newer timestamps than file1 self.create_regular_file('file2', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) # no listing by default output = self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') self.assert_not_in('file1', output) @@ -2068,7 +2070,7 @@ def fifo_feeder(fifo_fn, data): finally: os.close(fd) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) data = b'foobar' * 1000 fifo_fn = os.path.join(self.input_path, 'fifo') @@ -2091,20 +2093,20 @@ def fifo_feeder(fifo_fn, data): def test_create_read_special_broken_symlink(self): os.symlink('somewhere does not exist', os.path.join(self.input_path, 'link')) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', '--read-special', 'test', 'input') output = self.cmd(f'--repo={self.repository_location}', 'list', 'test') assert 'input/link -> somewhere does not exist' in output # def test_cmdline_compatibility(self): # self.create_regular_file('file1', size=1024 * 80) - # self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + # self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) # self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') # output = self.cmd('foo', self.repository_location, '--old') # self.assert_in('"--old" has been deprecated. Use "--new" instead', output) def test_prune_repository(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test1', src_dir) self.cmd(f'--repo={self.repository_location}', 'create', 'test2', src_dir) # these are not really a checkpoints, but they look like some: @@ -2153,7 +2155,7 @@ def _create_archive_ts(self, name, y, m, d, H=0, M=0, S=0): # This test must match docs/misc/prune-example.txt def test_prune_repository_example(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) # Archives that will be kept, per the example # Oldest archive self._create_archive_ts('test01', 2015, 1, 1) @@ -2214,7 +2216,7 @@ def test_prune_repository_example(self): # With an initial and daily backup, prune daily until oldest is replaced by a monthly backup def test_prune_retain_and_expire_oldest(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) # Initial backup self._create_archive_ts('original_archive', 2020, 9, 1, 11, 15) # Archive and prune daily for 30 days @@ -2237,7 +2239,7 @@ def test_prune_retain_and_expire_oldest(self): self.assert_not_in('original_archive', output) def test_prune_repository_save_space(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test1', src_dir) self.cmd(f'--repo={self.repository_location}', 'create', 'test2', src_dir) output = self.cmd(f'--repo={self.repository_location}', 'prune', '--list', '--dry-run', '--keep-daily=1') @@ -2252,7 +2254,7 @@ def test_prune_repository_save_space(self): self.assert_in('test2', output) def test_prune_repository_prefix(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'foo-2015-08-12-10:00', src_dir) self.cmd(f'--repo={self.repository_location}', 'create', 'foo-2015-08-12-20:00', src_dir) self.cmd(f'--repo={self.repository_location}', 'create', 'bar-2015-08-12-10:00', src_dir) @@ -2273,7 +2275,7 @@ def test_prune_repository_prefix(self): self.assert_in('bar-2015-08-12-20:00', output) def test_prune_repository_glob(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', '2015-08-12-10:00-foo', src_dir) self.cmd(f'--repo={self.repository_location}', 'create', '2015-08-12-20:00-foo', src_dir) self.cmd(f'--repo={self.repository_location}', 'create', '2015-08-12-10:00-bar', src_dir) @@ -2294,7 +2296,7 @@ def test_prune_repository_glob(self): self.assert_in('2015-08-12-20:00-bar', output) def test_list_prefix(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test-1', src_dir) self.cmd(f'--repo={self.repository_location}', 'create', 'something-else-than-test-1', src_dir) self.cmd(f'--repo={self.repository_location}', 'create', 'test-2', src_dir) @@ -2304,7 +2306,7 @@ def test_list_prefix(self): self.assert_not_in('something-else', output) def test_list_format(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', src_dir) output_1 = self.cmd(f'--repo={self.repository_location}', 'list', 'test') output_2 = self.cmd(f'--repo={self.repository_location}', 'list', 'test', '--format', '{mode} {user:6} {group:6} {size:8d} {mtime} {path}{extra}{NEWLINE}') @@ -2313,7 +2315,7 @@ def test_list_format(self): self.assertNotEqual(output_1, output_3) def test_archives_format(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', '--comment', 'comment 1', 'test-1', src_dir) self.cmd(f'--repo={self.repository_location}', 'create', '--comment', 'comment 2', 'test-2', src_dir) output_1 = self.cmd(f'--repo={self.repository_location}', 'rlist') @@ -2330,14 +2332,14 @@ def test_archives_format(self): def test_list_hash(self): self.create_regular_file('empty_file', size=0) self.create_regular_file('amb', contents=b'a' * 1000000) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') output = self.cmd(f'--repo={self.repository_location}', 'list', 'test', '--format', '{sha256} {path}{NL}') assert "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 input/amb" in output assert "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 input/empty_file" in output def test_list_consider_checkpoints(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test1', src_dir) # these are not really a checkpoints, but they look like some: self.cmd(f'--repo={self.repository_location}', 'create', 'test2.checkpoint', src_dir) @@ -2357,7 +2359,7 @@ def test_list_chunk_counts(self): with open(os.path.join(self.input_path, 'two_chunks'), 'wb') as fd: fd.write(b'abba' * 2000000) fd.write(b'baab' * 2000000) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') output = self.cmd(f'--repo={self.repository_location}', 'list', 'test', '--format', '{num_chunks} {unique_chunks} {path}{NL}') assert "0 0 input/empty_file" in output @@ -2365,7 +2367,7 @@ def test_list_chunk_counts(self): def test_list_size(self): self.create_regular_file('compressible_file', size=10000) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', '-C', 'lz4', 'test', 'input') output = self.cmd(f'--repo={self.repository_location}', 'list', 'test', '--format', '{size} {path}{NL}') size, path = output.split("\n")[1].split(" ") @@ -2373,13 +2375,13 @@ def test_list_size(self): def test_list_json(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') list_repo = json.loads(self.cmd(f'--repo={self.repository_location}', 'rlist', '--json')) repository = list_repo['repository'] assert len(repository['id']) == 64 assert datetime.strptime(repository['last_modified'], ISO_FORMAT) # must not raise - assert list_repo['encryption']['mode'] == 'repokey' + assert list_repo['encryption']['mode'] == RK_ENCRYPTION[13:] assert 'keyfile' not in list_repo['encryption'] archive0 = list_repo['archives'][0] assert datetime.strptime(archive0['time'], ISO_FORMAT) # must not raise @@ -2401,7 +2403,7 @@ def test_list_json(self): def test_log_json(self): self.create_test_files() - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) log = self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input', '--log-json', '--list', '--debug') messages = {} # type -> message, one of each kind for line in log.splitlines(): @@ -2419,7 +2421,7 @@ def test_log_json(self): def test_debug_profile(self): self.create_test_files() - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input', '--debug-profile=create.prof') self.cmd('debug', 'convert-profile', 'create.prof', 'create.pyprof') stats = pstats.Stats('create.pyprof') @@ -2433,12 +2435,12 @@ def test_debug_profile(self): def test_common_options(self): self.create_test_files() - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) log = self.cmd(f'--repo={self.repository_location}', '--debug', 'create', 'test', 'input') assert 'security: read previous location' in log def test_change_passphrase(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) os.environ['BORG_NEW_PASSPHRASE'] = 'newpassphrase' # here we have both BORG_PASSPHRASE and BORG_NEW_PASSPHRASE set: self.cmd(f'--repo={self.repository_location}', 'key', 'change-passphrase') @@ -2446,39 +2448,39 @@ def test_change_passphrase(self): self.cmd(f'--repo={self.repository_location}', 'rlist') def test_change_location_to_keyfile(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) log = self.cmd(f'--repo={self.repository_location}', 'rinfo') - assert '(repokey)' in log + assert '(repokey' in log self.cmd(f'--repo={self.repository_location}', 'key', 'change-location', 'keyfile') log = self.cmd(f'--repo={self.repository_location}', 'rinfo') - assert '(key file)' in log + assert '(key file' in log def test_change_location_to_b2keyfile(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey-blake2') + self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey-blake2-aes-ocb') log = self.cmd(f'--repo={self.repository_location}', 'rinfo') - assert '(repokey BLAKE2b)' in log + assert '(repokey BLAKE2b' in log self.cmd(f'--repo={self.repository_location}', 'key', 'change-location', 'keyfile') log = self.cmd(f'--repo={self.repository_location}', 'rinfo') - assert '(key file BLAKE2b)' in log + assert '(key file BLAKE2b' in log def test_change_location_to_repokey(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=keyfile') + self.cmd(f'--repo={self.repository_location}', 'rcreate', KF_ENCRYPTION) log = self.cmd(f'--repo={self.repository_location}', 'rinfo') - assert '(key file)' in log + assert '(key file' in log self.cmd(f'--repo={self.repository_location}', 'key', 'change-location', 'repokey') log = self.cmd(f'--repo={self.repository_location}', 'rinfo') - assert '(repokey)' in log + assert '(repokey' in log def test_change_location_to_b2repokey(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=keyfile-blake2') + self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=keyfile-blake2-aes-ocb') log = self.cmd(f'--repo={self.repository_location}', 'rinfo') - assert '(key file BLAKE2b)' in log + assert '(key file BLAKE2b' in log self.cmd(f'--repo={self.repository_location}', 'key', 'change-location', 'repokey') log = self.cmd(f'--repo={self.repository_location}', 'rinfo') - assert '(repokey BLAKE2b)' in log + assert '(repokey BLAKE2b' in log def test_break_lock(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'break-lock') def test_usage(self): @@ -2505,7 +2507,7 @@ def has_noatime(some_file): noatime_used = flags_noatime != flags_normal return noatime_used and atime_before == atime_after - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_test_files() have_noatime = has_noatime('input/file1') self.cmd(f'--repo={self.repository_location}', 'create', '--exclude-nodump', '--atime', 'archive', 'input') @@ -2594,7 +2596,7 @@ def has_noatime(some_file): @unittest.skipUnless(llfuse, 'llfuse not installed') def test_fuse_versions_view(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('test', contents=b'first') if are_hardlinks_supported(): self.create_regular_file('hardlink1', contents=b'123456') @@ -2626,7 +2628,7 @@ def test_fuse_versions_view(self): @unittest.skipUnless(llfuse, 'llfuse not installed') def test_fuse_allow_damaged_files(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('archive') # Get rid of a chunk and repair it archive, repository = self.open_archive('archive') @@ -2651,7 +2653,7 @@ def test_fuse_allow_damaged_files(self): @unittest.skipUnless(llfuse, 'llfuse not installed') def test_fuse_mount_options(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('arch11') self.create_src_archive('arch12') self.create_src_archive('arch21') @@ -2759,43 +2761,9 @@ def wrapper(self, old_id, new_id): # Undecorate borg.locking.Lock.migrate_lock = borg.locking.Lock.migrate_lock.__wrapped__ - def verify_aes_counter_uniqueness(self, method): - seen = set() # Chunks already seen - used = set() # counter values already used - - def verify_uniqueness(): - with Repository(self.repository_path) as repository: - for id, _ in repository.open_index(repository.get_transaction_id()).iteritems(): - data = repository.get(id) - hash = sha256(data).digest() - if hash not in seen: - seen.add(hash) - num_blocks = num_cipher_blocks(len(data) - 41) - nonce = bytes_to_long(data[33:41]) - for counter in range(nonce, nonce + num_blocks): - self.assert_not_in(counter, used) - used.add(counter) - - self.create_test_files() - os.environ['BORG_PASSPHRASE'] = 'passphrase' - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=' + method) - verify_uniqueness() - self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') - verify_uniqueness() - self.cmd(f'--repo={self.repository_location}', 'create', 'test.2', 'input') - verify_uniqueness() - self.cmd(f'--repo={self.repository_location}', 'delete', '-a', 'test.2') - verify_uniqueness() - - def test_aes_counter_uniqueness_keyfile(self): - self.verify_aes_counter_uniqueness('keyfile') - - def test_aes_counter_uniqueness_passphrase(self): - self.verify_aes_counter_uniqueness('repokey') - def test_debug_dump_archive_items(self): self.create_test_files() - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') with changedir('output'): output = self.cmd(f'--repo={self.repository_location}', 'debug', 'dump-archive-items', 'test') @@ -2805,7 +2773,7 @@ def test_debug_dump_archive_items(self): def test_debug_dump_repo_objs(self): self.create_test_files() - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') with changedir('output'): output = self.cmd(f'--repo={self.repository_location}', 'debug', 'dump-repo-objs') @@ -2814,7 +2782,7 @@ def test_debug_dump_repo_objs(self): assert 'Done.' in output def test_debug_put_get_delete_obj(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) data = b'some data' hexkey = sha256(data).hexdigest() self.create_regular_file('file', contents=data) @@ -2837,19 +2805,19 @@ def raise_eof(*args, **kwargs): raise EOFError with patch.object(FlexiKey, 'create', raise_eof): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey', exit_code=1) + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION, exit_code=1) assert not os.path.exists(self.repository_location) def test_init_requires_encryption_option(self): self.cmd(f'--repo={self.repository_location}', 'rcreate', exit_code=2) def test_init_nested_repositories(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) if self.FORK_DEFAULT: - self.cmd(f'--repo={self.repository_location}/nested', 'rcreate', '--encryption=repokey', exit_code=2) + self.cmd(f'--repo={self.repository_location}/nested', 'rcreate', RK_ENCRYPTION, exit_code=2) else: with pytest.raises(Repository.AlreadyExists): - self.cmd(f'--repo={self.repository_location}/nested', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}/nested', 'rcreate', RK_ENCRYPTION) def test_init_refuse_to_overwrite_keyfile(self): """BORG_KEY_FILE=something borg init should quit if "something" already exists. @@ -2857,10 +2825,10 @@ def test_init_refuse_to_overwrite_keyfile(self): See https://github.com/borgbackup/borg/pull/6046""" keyfile = os.path.join(self.tmpdir, 'keyfile') with environment_variable(BORG_KEY_FILE=keyfile): - self.cmd(f'--repo={self.repository_location}0', 'rcreate', '--encryption=keyfile') + self.cmd(f'--repo={self.repository_location}0', 'rcreate', KF_ENCRYPTION) with open(keyfile) as file: before = file.read() - arg = (f'--repo={self.repository_location}1', 'rcreate', '--encryption=keyfile') + arg = (f'--repo={self.repository_location}1', 'rcreate', KF_ENCRYPTION) if self.FORK_DEFAULT: self.cmd(*arg, exit_code=2) else: @@ -2892,7 +2860,7 @@ def check_cache(self): assert id in seen def test_check_cache(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') with self.open_repository() as repository: manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) @@ -2904,13 +2872,13 @@ def test_check_cache(self): self.check_cache() def test_recreate_target_rc(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'recreate', '--target=asdf', exit_code=2) assert 'Need to specify single archive' in output def test_recreate_target(self): self.create_test_files() - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.check_cache() self.cmd(f'--repo={self.repository_location}', 'create', 'test0', 'input') self.check_cache() @@ -2930,7 +2898,7 @@ def test_recreate_target(self): def test_recreate_basic(self): self.create_test_files() self.create_regular_file('dir2/file3', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test0', 'input') self.cmd(f'--repo={self.repository_location}', 'recreate', 'test0', 'input/dir2', '-e', 'input/dir2/file3') self.check_cache() @@ -2960,7 +2928,7 @@ def test_recreate_rechunkify(self): with open(os.path.join(self.input_path, 'large_file'), 'wb') as fd: fd.write(b'a' * 280) fd.write(b'b' * 280) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test1', 'input', '--chunker-params', '7,9,8,128') self.cmd(f'--repo={self.repository_location}', 'create', 'test2', 'input', '--files-cache=disabled') list = self.cmd(f'--repo={self.repository_location}', 'list', 'test1', 'input/large_file', @@ -2977,7 +2945,7 @@ def test_recreate_rechunkify(self): def test_recreate_recompress(self): self.create_regular_file('compressible', size=10000) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input', '-C', 'none') file_list = self.cmd(f'--repo={self.repository_location}', 'list', 'test', 'input/compressible', '--format', '{size} {sha256}') @@ -2992,7 +2960,7 @@ def test_recreate_recompress(self): def test_recreate_timestamp(self): local_timezone = datetime.now(timezone(timedelta(0))).astimezone().tzinfo self.create_test_files() - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test0', 'input') self.cmd(f'--repo={self.repository_location}', 'recreate', 'test0', '--timestamp', "1970-01-02T00:00:00", '--comment', 'test') @@ -3004,7 +2972,7 @@ def test_recreate_timestamp(self): def test_recreate_dry_run(self): self.create_regular_file('compressible', size=10000) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') archives_before = self.cmd(f'--repo={self.repository_location}', 'list', 'test') self.cmd(f'--repo={self.repository_location}', 'recreate', '-n', '-e', 'input/compressible') @@ -3014,7 +2982,7 @@ def test_recreate_dry_run(self): def test_recreate_skips_nothing_to_do(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') info_before = self.cmd(f'--repo={self.repository_location}', 'info', '-a', 'test') self.cmd(f'--repo={self.repository_location}', 'recreate', '--chunker-params', 'default') @@ -3023,13 +2991,13 @@ def test_recreate_skips_nothing_to_do(self): assert info_before == info_after # includes archive ID def test_with_lock(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) lock_path = os.path.join(self.repository_path, 'lock.exclusive') cmd = 'python3', '-c', 'import os, sys; sys.exit(42 if os.path.exists("%s") else 23)' % lock_path self.cmd(f'--repo={self.repository_location}', 'with-lock', *cmd, fork=True, exit_code=42) def test_recreate_list_output(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('file1', size=0) self.create_regular_file('file2', size=0) self.create_regular_file('file3', size=0) @@ -3059,13 +3027,13 @@ def test_recreate_list_output(self): self.assert_not_in("x input/file5", output) def test_bad_filters(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') self.cmd(f'--repo={self.repository_location}', 'delete', '--first', '1', '--last', '1', fork=True, exit_code=2) def test_key_export_keyfile(self): export_file = self.output_path + '/exported' - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption', 'keyfile') + self.cmd(f'--repo={self.repository_location}', 'rcreate', KF_ENCRYPTION) repo_id = self._extract_repository_id(self.repository_path) self.cmd(f'--repo={self.repository_location}', 'key', 'export', export_file) @@ -3091,7 +3059,7 @@ def test_key_export_keyfile(self): assert key_contents2 == key_contents def test_key_import_keyfile_with_borg_key_file(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption', 'keyfile') + self.cmd(f'--repo={self.repository_location}', 'rcreate', KF_ENCRYPTION) exported_key_file = os.path.join(self.output_path, 'exported') self.cmd(f'--repo={self.repository_location}', 'key', 'export', exported_key_file) @@ -3112,7 +3080,7 @@ def test_key_import_keyfile_with_borg_key_file(self): def test_key_export_repokey(self): export_file = self.output_path + '/exported' - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption', 'repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) repo_id = self._extract_repository_id(self.repository_path) self.cmd(f'--repo={self.repository_location}', 'key', 'export', export_file) @@ -3122,10 +3090,10 @@ def test_key_export_repokey(self): assert export_contents.startswith('BORG_KEY ' + bin_to_hex(repo_id) + '\n') with Repository(self.repository_path) as repository: - repo_key = RepoKey(repository) + repo_key = AESOCBRepoKey(repository) repo_key.load(None, Passphrase.env_passphrase()) - backup_key = KeyfileKey(key.TestKey.MockRepository()) + backup_key = AESOCBKeyfileKey(key.TestKey.MockRepository()) backup_key.load(export_file, Passphrase.env_passphrase()) assert repo_key.enc_key == backup_key.enc_key @@ -3136,14 +3104,14 @@ def test_key_export_repokey(self): self.cmd(f'--repo={self.repository_location}', 'key', 'import', export_file) with Repository(self.repository_path) as repository: - repo_key2 = RepoKey(repository) + repo_key2 = AESOCBRepoKey(repository) repo_key2.load(None, Passphrase.env_passphrase()) assert repo_key2.enc_key == repo_key2.enc_key def test_key_export_qr(self): export_file = self.output_path + '/exported.html' - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption', 'repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) repo_id = self._extract_repository_id(self.repository_path) self.cmd(f'--repo={self.repository_location}', 'key', 'export', '--qr-html', export_file) @@ -3158,13 +3126,13 @@ def test_key_export_directory(self): export_directory = self.output_path + '/exported' os.mkdir(export_directory) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption', 'repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'key', 'export', export_directory, exit_code=EXIT_ERROR) def test_key_import_errors(self): export_file = self.output_path + '/exported' - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption', 'keyfile') + self.cmd(f'--repo={self.repository_location}', 'rcreate', KF_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'key', 'import', export_file, exit_code=EXIT_ERROR) @@ -3190,13 +3158,13 @@ def test_key_export_paperkey(self): repo_id = 'e294423506da4e1ea76e8dcdf1a3919624ae3ae496fddf905610c351d3f09239' export_file = self.output_path + '/exported' - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption', 'keyfile') + self.cmd(f'--repo={self.repository_location}', 'rcreate', KF_ENCRYPTION) self._set_repository_id(self.repository_path, unhexlify(repo_id)) key_file = self.keys_path + '/' + os.listdir(self.keys_path)[0] with open(key_file, 'w') as fd: - fd.write(KeyfileKey.FILE_ID + ' ' + repo_id + '\n') + fd.write(CHPOKeyfileKey.FILE_ID + ' ' + repo_id + '\n') fd.write(b2a_base64(b'abcdefghijklmnopqrstu').decode()) self.cmd(f'--repo={self.repository_location}', 'key', 'export', '--paper', export_file) @@ -3214,12 +3182,12 @@ def test_key_export_paperkey(self): def test_key_import_paperkey(self): repo_id = 'e294423506da4e1ea76e8dcdf1a3919624ae3ae496fddf905610c351d3f09239' - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption', 'keyfile') + self.cmd(f'--repo={self.repository_location}', 'rcreate', KF_ENCRYPTION) self._set_repository_id(self.repository_path, unhexlify(repo_id)) key_file = self.keys_path + '/' + os.listdir(self.keys_path)[0] with open(key_file, 'w') as fd: - fd.write(KeyfileKey.FILE_ID + ' ' + repo_id + '\n') + fd.write(AESOCBKeyfileKey.FILE_ID + ' ' + repo_id + '\n') fd.write(b2a_base64(b'abcdefghijklmnopqrstu').decode()) typed_input = ( @@ -3259,7 +3227,7 @@ def test_key_import_paperkey(self): def test_debug_dump_manifest(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') dump_file = self.output_path + '/dump' output = self.cmd(f'--repo={self.repository_location}', 'debug', 'dump-manifest', dump_file) @@ -3274,7 +3242,7 @@ def test_debug_dump_manifest(self): def test_debug_dump_archive(self): self.create_regular_file('file1', size=1024 * 80) - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') dump_file = self.output_path + '/dump' output = self.cmd(f'--repo={self.repository_location}', 'debug', 'dump-archive', 'test', dump_file) @@ -3287,7 +3255,7 @@ def test_debug_dump_archive(self): assert '_items' in result def test_debug_refcount_obj(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'debug', 'refcount-obj', '0' * 64).strip() assert output == 'object 0000000000000000000000000000000000000000000000000000000000000000 not found [info from chunks cache].' @@ -3306,14 +3274,14 @@ def test_debug_info(self): assert 'Python' in output def test_benchmark_crud(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) with environment_variable(_BORG_BENCHMARK_CRUD_TEST='YES'): self.cmd(f'--repo={self.repository_location}', 'benchmark', 'crud', self.input_path) def test_config(self): self.create_test_files() os.unlink('input/flagfile') - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) output = self.cmd(f'--repo={self.repository_location}', 'config', '--list') self.assert_in('[repository]', output) self.assert_in('version', output) @@ -3356,7 +3324,7 @@ def test_config(self): def test_export_tar(self): self.create_test_files() os.unlink('input/flagfile') - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') self.cmd(f'--repo={self.repository_location}', 'export-tar', 'test', 'simple.tar', '--progress', '--tar-format=GNU') with changedir('output'): @@ -3371,7 +3339,7 @@ def test_export_tar_gz(self): pytest.skip('gzip is not installed') self.create_test_files() os.unlink('input/flagfile') - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') list = self.cmd(f'--repo={self.repository_location}', 'export-tar', 'test', 'simple.tar.gz', '--list', '--tar-format=GNU') @@ -3387,7 +3355,7 @@ def test_export_tar_strip_components(self): pytest.skip('gzip is not installed') self.create_test_files() os.unlink('input/flagfile') - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') list = self.cmd(f'--repo={self.repository_location}', 'export-tar', 'test', 'simple.tar', '--strip-components=1', '--list', '--tar-format=GNU') @@ -3497,69 +3465,22 @@ def test_do_not_mention_archive_if_you_can_not_find_repo(self): self.assert_in('this-repository-does-not-exist', output) self.assert_not_in('this-repository-does-not-exist::test', output) - def test_can_read_repo_even_if_nonce_is_deleted(self): - """Nonce is only used for encrypting new data. - - It should be possible to retrieve the data from an archive even if - both the client and the server forget the nonce""" - self.create_regular_file('file1', contents=b'Hello, borg') - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') - self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') - # Oops! We have removed the repo-side memory of the nonce! - # See https://github.com/borgbackup/borg/issues/5858 - os.remove(os.path.join(self.repository_path, 'nonce')) - # Oops! The client has lost the nonce too! - os.remove(os.path.join(self.get_security_dir(), 'nonce')) - - # The repo should still be readable - repo_info = self.cmd(f'--repo={self.repository_location}', 'rinfo') - assert 'Original size:' in repo_info - repo_list = self.cmd(f'--repo={self.repository_location}', 'rlist') - assert 'test' in repo_list - # The archive should still be readable - archive_info = self.cmd(f'--repo={self.repository_location}', 'info', '-a', 'test') - assert 'Archive name: test\n' in archive_info - archive_list = self.cmd(f'--repo={self.repository_location}', 'list', 'test') - assert 'file1' in archive_list - # Extracting the archive should work - with changedir('output'): - self.cmd(f'--repo={self.repository_location}', 'extract', 'test') - self.assert_dirs_equal('input', 'output/input') - - def test_recovery_from_deleted_repo_nonce(self): - """We should be able to recover if path/to/repo/nonce is deleted. - - The nonce is stored in two places: in the repo and in $HOME. - The nonce in the repo is only needed when multiple clients use the same - repo. Otherwise we can just use our own copy of the nonce. - """ - self.create_regular_file('file1', contents=b'Hello, borg') - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') - self.cmd(f'--repo={self.repository_location}', 'create', 'test', 'input') - # Oops! We have removed the repo-side memory of the nonce! - # See https://github.com/borgbackup/borg/issues/5858 - nonce = os.path.join(self.repository_path, 'nonce') - os.remove(nonce) - - self.cmd(f'--repo={self.repository_location}', 'create', 'test2', 'input') - assert os.path.exists(nonce) - def test_init_defaults_to_argon2(self): """https://github.com/borgbackup/borg/issues/747#issuecomment-1076160401""" - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) with Repository(self.repository_path) as repository: key = msgpack.unpackb(a2b_base64(repository.load_key())) assert key['algorithm'] == 'argon2 chacha20-poly1305' def test_init_with_explicit_key_algorithm(self): """https://github.com/borgbackup/borg/issues/747#issuecomment-1076160401""" - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey', '--key-algorithm=pbkdf2') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION, '--key-algorithm=pbkdf2') with Repository(self.repository_path) as repository: key = msgpack.unpackb(a2b_base64(repository.load_key())) assert key['algorithm'] == 'sha256' def verify_change_passphrase_does_not_change_algorithm(self, given_algorithm, expected_algorithm): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey', '--key-algorithm', given_algorithm) + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION, '--key-algorithm', given_algorithm) os.environ['BORG_NEW_PASSPHRASE'] = 'newpassphrase' self.cmd(f'--repo={self.repository_location}', 'key', 'change-passphrase') @@ -3575,7 +3496,7 @@ def test_change_passphrase_does_not_change_algorithm_pbkdf2(self): self.verify_change_passphrase_does_not_change_algorithm('pbkdf2', 'sha256') def verify_change_location_does_not_change_algorithm(self, given_algorithm, expected_algorithm): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=keyfile', '--key-algorithm', given_algorithm) + self.cmd(f'--repo={self.repository_location}', 'rcreate', KF_ENCRYPTION, '--key-algorithm', given_algorithm) self.cmd(f'--repo={self.repository_location}', 'key', 'change-location', 'repokey') @@ -3590,7 +3511,7 @@ def test_change_location_does_not_change_algorithm_pbkdf2(self): self.verify_change_location_does_not_change_algorithm('pbkdf2', 'sha256') def test_key_change_algorithm(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey', '--key-algorithm=pbkdf2') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION, '--key-algorithm=pbkdf2') self.cmd(f'--repo={self.repository_location}', 'key', 'change-algorithm', 'argon2') @@ -3649,7 +3570,7 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase): def setUp(self): super().setUp() with patch.object(ChunkBuffer, 'BUFFER_SIZE', 10): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('archive1') self.create_src_archive('archive2') @@ -3844,7 +3765,7 @@ def _test_verify_data(self, *init_args): assert 'testsuite/archiver.py: New missing file chunk detected' in output def test_verify_data(self): - self._test_verify_data('--encryption', 'repokey') + self._test_verify_data(RK_ENCRYPTION) def test_verify_data_unencrypted(self): self._test_verify_data('--encryption', 'none') @@ -3870,7 +3791,7 @@ def spoof_manifest(self, repository): repository.commit(compact=False) def test_fresh_init_tam_required(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) repository = Repository(self.repository_path, exclusive=True) with repository: manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) @@ -3885,7 +3806,7 @@ def test_fresh_init_tam_required(self): self.cmd(f'--repo={self.repository_location}', 'rlist') def test_not_required(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_src_archive('archive1234') repository = Repository(self.repository_path, exclusive=True) # Manifest must be authenticated now @@ -3908,32 +3829,32 @@ def open_repository(self): def test_remote_repo_restrict_to_path(self): # restricted to repo directory itself: with patch.object(RemoteRepository, 'extra_test_args', ['--restrict-to-path', self.repository_path]): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) # restricted to repo directory itself, fail for other directories with same prefix: with patch.object(RemoteRepository, 'extra_test_args', ['--restrict-to-path', self.repository_path]): with pytest.raises(PathNotAllowed): - self.cmd(f'--repo={self.repository_location}_0', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}_0', 'rcreate', RK_ENCRYPTION) # restricted to a completely different path: with patch.object(RemoteRepository, 'extra_test_args', ['--restrict-to-path', '/foo']): with pytest.raises(PathNotAllowed): - self.cmd(f'--repo={self.repository_location}_1', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}_1', 'rcreate', RK_ENCRYPTION) path_prefix = os.path.dirname(self.repository_path) # restrict to repo directory's parent directory: with patch.object(RemoteRepository, 'extra_test_args', ['--restrict-to-path', path_prefix]): - self.cmd(f'--repo={self.repository_location}_2', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}_2', 'rcreate', RK_ENCRYPTION) # restrict to repo directory's parent directory and another directory: with patch.object(RemoteRepository, 'extra_test_args', ['--restrict-to-path', '/foo', '--restrict-to-path', path_prefix]): - self.cmd(f'--repo={self.repository_location}_3', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}_3', 'rcreate', RK_ENCRYPTION) def test_remote_repo_restrict_to_repository(self): # restricted to repo directory itself: with patch.object(RemoteRepository, 'extra_test_args', ['--restrict-to-repository', self.repository_path]): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) parent_path = os.path.join(self.repository_path, '..') with patch.object(RemoteRepository, 'extra_test_args', ['--restrict-to-repository', parent_path]): with pytest.raises(PathNotAllowed): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) @unittest.skip('only works locally') def test_debug_put_get_delete_obj(self): @@ -3948,7 +3869,7 @@ def test_migrate_lock_alive(self): pass def test_remote_repo_strip_components_doesnt_leak(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('dir/file', contents=b"test file contents 1") self.create_regular_file('dir/file2', contents=b"test file contents 2") self.create_regular_file('skipped-file1', contents=b"test file contents 3") @@ -3974,7 +3895,7 @@ class ArchiverCorruptionTestCase(ArchiverTestCaseBase): def setUp(self): super().setUp() self.create_test_files() - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.cache_path = json.loads(self.cmd(f'--repo={self.repository_location}', 'rinfo', '--json'))['cache']['path'] def corrupt(self, file, amount=1): @@ -4068,7 +3989,7 @@ def test_basic_functionality(self): os.link('input/file_removed', 'input/hardlink_removed') os.link('input/file_removed2', 'input/hardlink_target_removed') - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) # Create the first snapshot self.cmd(f'--repo={self.repository_location}', 'create', 'test0', 'input') @@ -4254,7 +4175,7 @@ def get_changes(filename, data): do_json_asserts(self.cmd(f'--repo={self.repository_location}', 'diff', 'test0', 'test1a', '--json-lines'), True) def test_sort_option(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', '--encryption=repokey') + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) self.create_regular_file('a_file_removed', size=8) self.create_regular_file('f_file_removed', size=16) @@ -4315,7 +4236,7 @@ def test_get_args(): assert args.restrict_to_repositories == ['/r1', '/r2'] # trying to cheat - try to execute different subcommand args = archiver.get_args(['borg', 'serve', '--restrict-to-path=/p1', '--restrict-to-path=/p2', ], - 'borg --repo=/ rcreate --encryption=repokey') + f'borg --repo=/ rcreate {RK_ENCRYPTION}') assert args.func == archiver.do_serve # Check that environment variables in the forced command don't cause issues. If the command From ef24dafb151f38ec16e11e520c760128212d9a82 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 30 Jun 2022 00:15:54 +0200 Subject: [PATCH 2/3] tests: use less RepoKey/KeyfileKey --- src/borg/crypto/keymanager.py | 10 +++---- src/borg/testsuite/cache.py | 4 +-- src/borg/testsuite/crypto.py | 10 +++---- src/borg/testsuite/key.py | 50 ++++++++++++++--------------------- 4 files changed, 32 insertions(+), 42 deletions(-) diff --git a/src/borg/crypto/keymanager.py b/src/borg/crypto/keymanager.py index 2d41c3022d..db5eb23c74 100644 --- a/src/borg/crypto/keymanager.py +++ b/src/borg/crypto/keymanager.py @@ -7,7 +7,7 @@ from ..helpers import Manifest, NoManifestError, Error, yes, bin_to_hex, dash_open from ..repository import Repository -from .key import KeyfileKey, KeyfileNotFoundError, RepoKeyNotFoundError, KeyBlobStorage, identify_key +from .key import CHPOKeyfileKey, KeyfileNotFoundError, RepoKeyNotFoundError, KeyBlobStorage, identify_key class UnencryptedRepo(Error): @@ -50,7 +50,7 @@ def __init__(self, repository): def load_keyblob(self): if self.keyblob_storage == KeyBlobStorage.KEYFILE: - k = KeyfileKey(self.repository) + k = CHPOKeyfileKey(self.repository) target = k.find_key() with open(target) as fd: self.keyblob = ''.join(fd.readlines()[1:]) @@ -65,7 +65,7 @@ def load_keyblob(self): def store_keyblob(self, args): if self.keyblob_storage == KeyBlobStorage.KEYFILE: - k = KeyfileKey(self.repository) + k = CHPOKeyfileKey(self.repository) target = k.get_existing_or_new_target(args) self.store_keyfile(target) @@ -73,7 +73,7 @@ def store_keyblob(self, args): self.repository.save_key(self.keyblob.encode('utf-8')) def get_keyfile_data(self): - data = f'{KeyfileKey.FILE_ID} {bin_to_hex(self.repository.id)}\n' + data = f'{CHPOKeyfileKey.FILE_ID} {bin_to_hex(self.repository.id)}\n' data += self.keyblob if not self.keyblob.endswith('\n'): data += '\n' @@ -136,7 +136,7 @@ def grouped(s): fd.write(export) def import_keyfile(self, args): - file_id = KeyfileKey.FILE_ID + file_id = CHPOKeyfileKey.FILE_ID first_line = file_id + ' ' + bin_to_hex(self.repository.id) + '\n' with dash_open(args.path, 'r') as fd: file_first_line = fd.read(len(first_line)) diff --git a/src/borg/testsuite/cache.py b/src/borg/testsuite/cache.py index faff9f2b79..f642a588e2 100644 --- a/src/borg/testsuite/cache.py +++ b/src/borg/testsuite/cache.py @@ -10,7 +10,7 @@ from ..archive import Statistics from ..cache import AdHocCache from ..compress import CompressionSpec -from ..crypto.key import RepoKey +from ..crypto.key import AESOCBRepoKey from ..hashindex import ChunkIndex, CacheSynchronizer from ..helpers import Manifest from ..repository import Repository @@ -218,7 +218,7 @@ def repository(self, tmpdir): @pytest.fixture def key(self, repository, monkeypatch): monkeypatch.setenv('BORG_PASSPHRASE', 'test') - key = RepoKey.create(repository, TestKey.MockArgs()) + key = AESOCBRepoKey.create(repository, TestKey.MockArgs()) key.compressor = CompressionSpec('none').compressor return key diff --git a/src/borg/testsuite/crypto.py b/src/borg/testsuite/crypto.py index e691c32109..e131f63ac6 100644 --- a/src/borg/testsuite/crypto.py +++ b/src/borg/testsuite/crypto.py @@ -9,7 +9,7 @@ from ..crypto.low_level import bytes_to_long, bytes_to_int, long_to_bytes from ..crypto.low_level import hkdf_hmac_sha512 from ..crypto.low_level import AES, hmac_sha256 -from ..crypto.key import KeyfileKey, RepoKey, FlexiKey +from ..crypto.key import CHPOKeyfileKey, AESOCBRepoKey, FlexiKey from ..helpers import msgpack from . import BaseTestCase @@ -264,7 +264,7 @@ def test_decrypt_key_file_argon2_chacha20_poly1305(): 'algorithm': 'argon2 chacha20-poly1305', 'data': envelope, }) - key = KeyfileKey(None) + key = CHPOKeyfileKey(None) decrypted = key.decrypt_key_file(encrypted, "hello, pass phrase") @@ -286,7 +286,7 @@ def test_decrypt_key_file_pbkdf2_sha256_aes256_ctr_hmac_sha256(): 'data': data, 'hash': hash, }) - key = KeyfileKey(None) + key = CHPOKeyfileKey(None) decrypted = key.decrypt_key_file(encrypted, passphrase) @@ -325,7 +325,7 @@ def test_repo_key_detect_does_not_raise_integrity_error(getpass, monkeypatch): repository = MagicMock(id=b'repository_id') getpass.return_value = "hello, pass phrase" monkeypatch.setenv('BORG_DISPLAY_PASSPHRASE', 'no') - RepoKey.create(repository, args=MagicMock(key_algorithm='argon2')) + AESOCBRepoKey.create(repository, args=MagicMock(key_algorithm='argon2')) repository.load_key.return_value = repository.save_key.call_args.args[0] - RepoKey.detect(repository, manifest_data=None) + AESOCBRepoKey.detect(repository, manifest_data=None) diff --git a/src/borg/testsuite/key.py b/src/borg/testsuite/key.py index 02eaa86e5e..01db163354 100644 --- a/src/borg/testsuite/key.py +++ b/src/borg/testsuite/key.py @@ -8,9 +8,10 @@ import pytest from ..crypto.key import bin_to_hex -from ..crypto.key import PlaintextKey, AuthenticatedKey, RepoKey, KeyfileKey, \ - Blake2KeyfileKey, Blake2RepoKey, Blake2AuthenticatedKey, \ - AESOCBKeyfileKey, AESOCBRepoKey, CHPOKeyfileKey, CHPORepoKey +from ..crypto.key import PlaintextKey, AuthenticatedKey, Blake2AuthenticatedKey +from ..crypto.key import RepoKey, KeyfileKey, Blake2RepoKey, Blake2KeyfileKey +from ..crypto.key import AESOCBRepoKey, AESOCBKeyfileKey, CHPORepoKey, CHPOKeyfileKey +from ..crypto.key import Blake2AESOCBRepoKey, Blake2AESOCBKeyfileKey, Blake2CHPORepoKey, Blake2CHPOKeyfileKey from ..crypto.key import ID_HMAC_SHA_256, ID_BLAKE2b_256 from ..crypto.key import TAMRequiredError, TAMInvalid, TAMUnsupportedSuiteError, UnsupportedManifestError, UnsupportedKeyFormatError from ..crypto.key import identify_key @@ -76,16 +77,17 @@ def keys_dir(self, request, monkeypatch, tmpdir): return tmpdir @pytest.fixture(params=( + # not encrypted PlaintextKey, - AuthenticatedKey, - KeyfileKey, - RepoKey, - AuthenticatedKey, - Blake2KeyfileKey, - Blake2RepoKey, - Blake2AuthenticatedKey, + AuthenticatedKey, Blake2AuthenticatedKey, + # legacy crypto + KeyfileKey, Blake2KeyfileKey, + RepoKey, Blake2RepoKey, + # new crypto AESOCBKeyfileKey, AESOCBRepoKey, + Blake2AESOCBKeyfileKey, Blake2AESOCBRepoKey, CHPOKeyfileKey, CHPORepoKey, + Blake2CHPOKeyfileKey, Blake2CHPORepoKey, )) def key(self, request, monkeypatch): monkeypatch.setenv('BORG_PASSPHRASE', 'test') @@ -143,33 +145,21 @@ def test_keyfile(self, monkeypatch, keys_dir): id = key.id_hash(chunk) assert chunk == key2.decrypt(id, key.encrypt(id, chunk)) - def test_keyfile_nonce_rollback_protection(self, monkeypatch, keys_dir): - monkeypatch.setenv('BORG_PASSPHRASE', 'test') - repository = self.MockRepository() - with open(os.path.join(get_security_dir(repository.id_str), 'nonce'), "w") as fd: - fd.write("0000000000002000") - key = KeyfileKey.create(repository, self.MockArgs()) - chunk = b'ABC' - id = key.id_hash(chunk) - data = key.encrypt(id, chunk) - assert key.cipher.extract_iv(data) == 0x2000 - assert key.decrypt(id, data) == chunk - def test_keyfile_kfenv(self, tmpdir, monkeypatch): keyfile = tmpdir.join('keyfile') monkeypatch.setenv('BORG_KEY_FILE', str(keyfile)) monkeypatch.setenv('BORG_PASSPHRASE', 'testkf') assert not keyfile.exists() - key = KeyfileKey.create(self.MockRepository(), self.MockArgs()) + key = CHPOKeyfileKey.create(self.MockRepository(), self.MockArgs()) assert keyfile.exists() chunk = b'ABC' chunk_id = key.id_hash(chunk) chunk_cdata = key.encrypt(chunk_id, chunk) - key = KeyfileKey.detect(self.MockRepository(), chunk_cdata) + key = CHPOKeyfileKey.detect(self.MockRepository(), chunk_cdata) assert chunk == key.decrypt(chunk_id, chunk_cdata) keyfile.remove() with pytest.raises(FileNotFoundError): - KeyfileKey.detect(self.MockRepository(), chunk_cdata) + CHPOKeyfileKey.detect(self.MockRepository(), chunk_cdata) def test_keyfile2(self, monkeypatch, keys_dir): with keys_dir.join('keyfile').open('w') as fd: @@ -275,7 +265,7 @@ class TestTAM: @pytest.fixture def key(self, monkeypatch): monkeypatch.setenv('BORG_PASSPHRASE', 'test') - return KeyfileKey.create(TestKey.MockRepository(), TestKey.MockArgs()) + return CHPOKeyfileKey.create(TestKey.MockRepository(), TestKey.MockArgs()) def test_unpack_future(self, key): blob = b'\xc1\xc1\xc1\xc1foobar' @@ -385,7 +375,7 @@ def test_tampered(self, key, which): def test_decrypt_key_file_unsupported_algorithm(): """We will add more algorithms in the future. We should raise a helpful error.""" - key = KeyfileKey(None) + key = CHPOKeyfileKey(None) encrypted = msgpack.packb({ 'algorithm': 'THIS ALGORITHM IS NOT SUPPORTED', 'version': 1, @@ -397,7 +387,7 @@ def test_decrypt_key_file_unsupported_algorithm(): def test_decrypt_key_file_v2_is_unsupported(): """There may eventually be a version 2 of the format. For now we should raise a helpful error.""" - key = KeyfileKey(None) + key = CHPOKeyfileKey(None) encrypted = msgpack.packb({ 'version': 2, }) @@ -415,10 +405,10 @@ def to_dict(key): repository = MagicMock(id=b'repository_id') monkeypatch.setenv('BORG_PASSPHRASE', "hello, pass phrase") - save_me = RepoKey.create(repository, args=MagicMock(key_algorithm=cli_argument)) + save_me = AESOCBRepoKey.create(repository, args=MagicMock(key_algorithm=cli_argument)) saved = repository.save_key.call_args.args[0] repository.load_key.return_value = saved - load_me = RepoKey.detect(repository, manifest_data=None) + load_me = AESOCBRepoKey.detect(repository, manifest_data=None) assert to_dict(load_me) == to_dict(save_me) assert msgpack.unpackb(a2b_base64(saved))['algorithm'] == expected_algorithm From eabad3e3b7fc0c301e0b390a141dcbaad28095ff Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 30 Jun 2022 01:13:49 +0200 Subject: [PATCH 3/3] rcreate: always use argon2 kdf for new repos, fixes #6820 this way, we can remove the legacy pbkdf2 key code in next release. --- setup_docs.py | 1 - src/borg/archiver.py | 58 ---------------------------------- src/borg/crypto/key.py | 2 +- src/borg/crypto/low_level.pyx | 2 +- src/borg/testsuite/archiver.py | 41 ++++-------------------- src/borg/testsuite/key.py | 7 ++-- 6 files changed, 11 insertions(+), 100 deletions(-) diff --git a/setup_docs.py b/setup_docs.py index 2f9dcb35df..7d985ebffc 100644 --- a/setup_docs.py +++ b/setup_docs.py @@ -303,7 +303,6 @@ class build_man(Command): 'key_change-passphrase': 'key', 'key_change-location': 'key', - 'key_change-algorithm': 'key', 'key_export': 'key', 'key_import': 'key', 'key_migrate-to-repokey': 'key', diff --git a/src/borg/archiver.py b/src/borg/archiver.py index 1bb4c14873..d61ead194d 100644 --- a/src/borg/archiver.py +++ b/src/borg/archiver.py @@ -4380,22 +4380,6 @@ 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 an `authenticated` mode. It's like `repokey` minus encryption. - Key derivation functions - ++++++++++++++++++++++++ - - - ``--key-algorithm argon2`` is the default and is recommended. - The key encryption key is derived from your passphrase via argon2-id. - Argon2 is considered more modern and secure than pbkdf2. - - Our implementation of argon2-based key algorithm follows the cryptographic best practices: - - - It derives two separate keys from your passphrase: one to encrypt your key and another one - to sign it. ``--key-algorithm pbkdf2`` uses the same key for both. - - - It uses encrypt-then-mac instead of encrypt-and-mac used by ``--key-algorithm pbkdf2`` - - Neither is inherently linked to the key derivation function, but since we were going - to break backwards compatibility anyway we took the opportunity to fix all 3 issues at once. """) subparser = subparsers.add_parser('rcreate', parents=[common_parser], add_help=False, description=self.do_rcreate.__doc__, epilog=rcreate_epilog, @@ -4418,8 +4402,6 @@ def define_borg_mount(parser): help='Set storage quota of the new repository (e.g. 5G, 1.5T). Default: no quota.') subparser.add_argument('--make-parent-dirs', dest='make_parent_dirs', action='store_true', help='create the parent directories of the repository directory, if they are missing.') - subparser.add_argument('--key-algorithm', dest='key_algorithm', default='argon2', choices=list(KEY_ALGORITHMS), - help='the algorithm we use to derive a key encryption key from your passphrase. Default: argon2') # borg key subparser = subparsers.add_parser('key', parents=[mid_common_parser], add_help=False, @@ -4544,46 +4526,6 @@ def define_borg_mount(parser): subparser.add_argument('--keep', dest='keep', action='store_true', help='keep the key also at the current location (default: remove it)') - change_algorithm_epilog = process_epilog(""" - Change the algorithm we use to encrypt and authenticate the borg key. - - Important: In a `repokey` mode (e.g. repokey-blake2) all users share the same key. - In this mode upgrading to `argon2` will make it impossible to access the repo for users who use an old version of borg. - We recommend upgrading to the latest stable version. - - Important: In a `keyfile` mode (e.g. keyfile-blake2) each user has their own key (in ``~/.config/borg/keys``). - In this mode this command will only change the key used by the current user. - If you want to upgrade to `argon2` to strengthen security, you will have to upgrade each user's key individually. - - Your repository is encrypted and authenticated with a key that is randomly generated by ``borg init``. - The key is encrypted and authenticated with your passphrase. - - We currently support two choices: - - 1. argon2 - recommended. This algorithm is used by default when initialising a new repository. - The key encryption key is derived from your passphrase via argon2-id. - Argon2 is considered more modern and secure than pbkdf2. - 2. pbkdf2 - the legacy algorithm. Use this if you want to access your repo via old versions of borg. - The key encryption key is derived from your passphrase via PBKDF2-HMAC-SHA256. - - Examples:: - - # Upgrade an existing key to argon2 - borg key change-algorithm /path/to/repo argon2 - # Downgrade to pbkdf2 - use this if upgrading borg is not an option - borg key change-algorithm /path/to/repo pbkdf2 - - - """) - subparser = key_parsers.add_parser('change-algorithm', parents=[common_parser], add_help=False, - description=self.do_change_algorithm.__doc__, - epilog=change_algorithm_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, - help='change key algorithm') - subparser.set_defaults(func=self.do_change_algorithm) - subparser.add_argument('algorithm', metavar='ALGORITHM', choices=list(KEY_ALGORITHMS), - help='select key algorithm') - # borg list list_epilog = process_epilog(""" This command lists the contents of an archive. diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index e1c58c47af..cd1fd9324f 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -620,7 +620,7 @@ def create(cls, repository, args, *, other_key=None): passphrase = Passphrase.new(allow_empty=True) key.init_ciphers() target = key.get_new_target(args) - key.save(target, passphrase, create=True, algorithm=KEY_ALGORITHMS[args.key_algorithm]) + key.save(target, passphrase, create=True, algorithm=KEY_ALGORITHMS['argon2']) logger.info('Key in "%s" created.' % target) logger.info('Keep this key safe. Your data will be inaccessible without it.') return key diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index c0818abc51..d44160b504 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -613,7 +613,7 @@ cdef class CHACHA20_POLY1305(_AEAD_BASE): super().__init__(key, iv=iv, header_len=header_len, aad_offset=aad_offset) -cdef class AES: +cdef class AES: # legacy """A thin wrapper around the OpenSSL EVP cipher API - for legacy code, like key file encryption""" cdef CIPHER cipher cdef EVP_CIPHER_CTX *ctx diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index 7c34da2380..6fda62be79 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -3472,53 +3472,24 @@ def test_init_defaults_to_argon2(self): key = msgpack.unpackb(a2b_base64(repository.load_key())) assert key['algorithm'] == 'argon2 chacha20-poly1305' - def test_init_with_explicit_key_algorithm(self): - """https://github.com/borgbackup/borg/issues/747#issuecomment-1076160401""" - self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION, '--key-algorithm=pbkdf2') - with Repository(self.repository_path) as repository: - key = msgpack.unpackb(a2b_base64(repository.load_key())) - assert key['algorithm'] == 'sha256' - - def verify_change_passphrase_does_not_change_algorithm(self, given_algorithm, expected_algorithm): - self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION, '--key-algorithm', given_algorithm) + def test_change_passphrase_does_not_change_algorithm_argon2(self): + self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION) os.environ['BORG_NEW_PASSPHRASE'] = 'newpassphrase' self.cmd(f'--repo={self.repository_location}', 'key', 'change-passphrase') with Repository(self.repository_path) as repository: key = msgpack.unpackb(a2b_base64(repository.load_key())) - assert key['algorithm'] == expected_algorithm - - def test_change_passphrase_does_not_change_algorithm_argon2(self): - self.verify_change_passphrase_does_not_change_algorithm('argon2', 'argon2 chacha20-poly1305') + assert key['algorithm'] == 'argon2 chacha20-poly1305' - def test_change_passphrase_does_not_change_algorithm_pbkdf2(self): - self.verify_change_passphrase_does_not_change_algorithm('pbkdf2', 'sha256') - - def verify_change_location_does_not_change_algorithm(self, given_algorithm, expected_algorithm): - self.cmd(f'--repo={self.repository_location}', 'rcreate', KF_ENCRYPTION, '--key-algorithm', given_algorithm) + def test_change_location_does_not_change_algorithm_argon2(self): + self.cmd(f'--repo={self.repository_location}', 'rcreate', KF_ENCRYPTION) self.cmd(f'--repo={self.repository_location}', 'key', 'change-location', 'repokey') with Repository(self.repository_path) as repository: key = msgpack.unpackb(a2b_base64(repository.load_key())) - assert key['algorithm'] == expected_algorithm - - def test_change_location_does_not_change_algorithm_argon2(self): - self.verify_change_location_does_not_change_algorithm('argon2', 'argon2 chacha20-poly1305') - - def test_change_location_does_not_change_algorithm_pbkdf2(self): - self.verify_change_location_does_not_change_algorithm('pbkdf2', 'sha256') - - def test_key_change_algorithm(self): - self.cmd(f'--repo={self.repository_location}', 'rcreate', RK_ENCRYPTION, '--key-algorithm=pbkdf2') - - self.cmd(f'--repo={self.repository_location}', 'key', 'change-algorithm', 'argon2') - - with Repository(self.repository_path) as repository: - _, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - assert key._encrypted_key_algorithm == 'argon2 chacha20-poly1305' - self.cmd(f'--repo={self.repository_location}', 'rinfo') + assert key['algorithm'] == 'argon2 chacha20-poly1305' @unittest.skipUnless('binary' in BORG_EXES, 'no borg.exe available') diff --git a/src/borg/testsuite/key.py b/src/borg/testsuite/key.py index 01db163354..b202d10784 100644 --- a/src/borg/testsuite/key.py +++ b/src/borg/testsuite/key.py @@ -396,8 +396,7 @@ def test_decrypt_key_file_v2_is_unsupported(): key.decrypt_key_file(encrypted, "hello, pass phrase") -@pytest.mark.parametrize('cli_argument, expected_algorithm', KEY_ALGORITHMS.items()) -def test_key_file_roundtrip(monkeypatch, cli_argument, expected_algorithm): +def test_key_file_roundtrip(monkeypatch): def to_dict(key): extract = 'repository_id', 'enc_key', 'enc_hmac_key', 'id_key', 'chunk_seed' return {a: getattr(key, a) for a in extract} @@ -405,10 +404,10 @@ def to_dict(key): repository = MagicMock(id=b'repository_id') monkeypatch.setenv('BORG_PASSPHRASE', "hello, pass phrase") - save_me = AESOCBRepoKey.create(repository, args=MagicMock(key_algorithm=cli_argument)) + save_me = AESOCBRepoKey.create(repository, args=MagicMock(key_algorithm='argon2')) saved = repository.save_key.call_args.args[0] repository.load_key.return_value = saved load_me = AESOCBRepoKey.detect(repository, manifest_data=None) assert to_dict(load_me) == to_dict(save_me) - assert msgpack.unpackb(a2b_base64(saved))['algorithm'] == expected_algorithm + assert msgpack.unpackb(a2b_base64(saved))['algorithm'] == KEY_ALGORITHMS['argon2']