mirror of
https://github.com/borgbackup/borg.git
synced 2026-04-09 19:06:30 -04:00
Merge pull request #6821 from ThomasWaldmann/remove-legacy-repo-creation-borg2
rcreate: remove legacy crypto for new repos, fixes #6490
This commit is contained in:
commit
2ab254cea0
9 changed files with 266 additions and 431 deletions
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ try:
|
|||
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 @@ class Archiver:
|
|||
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)
|
||||
|
||||
|
|
@ -4374,22 +4380,6 @@ class Archiver:
|
|||
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,
|
||||
|
|
@ -4412,8 +4402,6 @@ class Archiver:
|
|||
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,
|
||||
|
|
@ -4538,46 +4526,6 @@ class Archiver:
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
@ -620,7 +620,7 @@ class FlexiKey:
|
|||
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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from hashlib import sha256
|
|||
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 @@ class KeyManager:
|
|||
|
||||
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 @@ class KeyManager:
|
|||
|
||||
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 @@ class KeyManager:
|
|||
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 @@ class KeyManager:
|
|||
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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -10,7 +10,7 @@ from .key import TestKey
|
|||
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 @@ class TestAdHocCache:
|
|||
@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
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from ..crypto.low_level import AES256_CTR_HMAC_SHA256, AES256_OCB, CHACHA20_POLY
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ from unittest.mock import MagicMock
|
|||
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 @@ class TestKey:
|
|||
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 @@ class TestKey:
|
|||
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 @@ class TestTAM:
|
|||
|
||||
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,
|
||||
})
|
||||
|
|
@ -406,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}
|
||||
|
|
@ -415,10 +404,10 @@ def test_key_file_roundtrip(monkeypatch, cli_argument, expected_algorithm):
|
|||
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='argon2'))
|
||||
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
|
||||
assert msgpack.unpackb(a2b_base64(saved))['algorithm'] == KEY_ALGORITHMS['argon2']
|
||||
|
|
|
|||
Loading…
Reference in a new issue