|
@@ -87,45 +87,38 @@ class TAMUnsupportedSuiteError(IntegrityError):
|
|
|
traceback = False
|
|
|
|
|
|
|
|
|
+class KeyBlobStorage:
|
|
|
+ NO_STORAGE = 'no_storage'
|
|
|
+ KEYFILE = 'keyfile'
|
|
|
+ REPO = 'repository'
|
|
|
+
|
|
|
+
|
|
|
def key_creator(repository, args):
|
|
|
- if args.encryption == 'keyfile':
|
|
|
- return KeyfileKey.create(repository, args)
|
|
|
- elif args.encryption == 'repokey':
|
|
|
- return RepoKey.create(repository, args)
|
|
|
- elif args.encryption == 'keyfile-blake2':
|
|
|
- return Blake2KeyfileKey.create(repository, args)
|
|
|
- elif args.encryption == 'repokey-blake2':
|
|
|
- return Blake2RepoKey.create(repository, args)
|
|
|
- elif args.encryption == 'authenticated':
|
|
|
- return AuthenticatedKey.create(repository, args)
|
|
|
- elif args.encryption == 'none':
|
|
|
- return PlaintextKey.create(repository, args)
|
|
|
+ for key in AVAILABLE_KEY_TYPES:
|
|
|
+ if key.ARG_NAME == args.encryption:
|
|
|
+ return key.create(repository, args)
|
|
|
else:
|
|
|
raise ValueError('Invalid encryption mode "%s"' % args.encryption)
|
|
|
|
|
|
|
|
|
-def key_factory(repository, manifest_data):
|
|
|
+def identify_key(manifest_data):
|
|
|
key_type = manifest_data[0]
|
|
|
- if key_type == KeyfileKey.TYPE:
|
|
|
- return KeyfileKey.detect(repository, manifest_data)
|
|
|
- elif key_type == RepoKey.TYPE:
|
|
|
- return RepoKey.detect(repository, manifest_data)
|
|
|
- elif key_type == PassphraseKey.TYPE:
|
|
|
+ if key_type == PassphraseKey.TYPE:
|
|
|
# we just dispatch to repokey mode and assume the passphrase was migrated to a repokey.
|
|
|
# see also comment in PassphraseKey class.
|
|
|
- return RepoKey.detect(repository, manifest_data)
|
|
|
- elif key_type == PlaintextKey.TYPE:
|
|
|
- return PlaintextKey.detect(repository, manifest_data)
|
|
|
- elif key_type == Blake2KeyfileKey.TYPE:
|
|
|
- return Blake2KeyfileKey.detect(repository, manifest_data)
|
|
|
- elif key_type == Blake2RepoKey.TYPE:
|
|
|
- return Blake2RepoKey.detect(repository, manifest_data)
|
|
|
- elif key_type == AuthenticatedKey.TYPE:
|
|
|
- return AuthenticatedKey.detect(repository, manifest_data)
|
|
|
+ return RepoKey
|
|
|
+
|
|
|
+ for key in AVAILABLE_KEY_TYPES:
|
|
|
+ if key.TYPE == key_type:
|
|
|
+ return key
|
|
|
else:
|
|
|
raise UnsupportedPayloadError(key_type)
|
|
|
|
|
|
|
|
|
+def key_factory(repository, manifest_data):
|
|
|
+ return identify_key(manifest_data).detect(repository, manifest_data)
|
|
|
+
|
|
|
+
|
|
|
def tam_required_file(repository):
|
|
|
security_dir = get_security_dir(bin_to_hex(repository.id))
|
|
|
return os.path.join(security_dir, 'tam_required')
|
|
@@ -139,6 +132,13 @@ def tam_required(repository):
|
|
|
class KeyBase:
|
|
|
TYPE = None # override in subclasses
|
|
|
|
|
|
+ # Human-readable name
|
|
|
+ NAME = 'UNDEFINED'
|
|
|
+ # Name used in command line / API (e.g. borg init --encryption=...)
|
|
|
+ ARG_NAME = 'UNDEFINED'
|
|
|
+ # Storage type (no key blob storage / keyfile / repo)
|
|
|
+ STORAGE = KeyBlobStorage.NO_STORAGE
|
|
|
+
|
|
|
def __init__(self, repository):
|
|
|
self.TYPE_STR = bytes([self.TYPE])
|
|
|
self.repository = repository
|
|
@@ -236,6 +236,8 @@ class KeyBase:
|
|
|
class PlaintextKey(KeyBase):
|
|
|
TYPE = 0x02
|
|
|
NAME = 'plaintext'
|
|
|
+ ARG_NAME = 'none'
|
|
|
+ STORAGE = KeyBlobStorage.NO_STORAGE
|
|
|
|
|
|
chunk_seed = 0
|
|
|
|
|
@@ -466,6 +468,9 @@ class PassphraseKey(ID_HMAC_SHA_256, AESKeyBase):
|
|
|
# This class is kept for a while to support migration from passphrase to repokey mode.
|
|
|
TYPE = 0x01
|
|
|
NAME = 'passphrase'
|
|
|
+ ARG_NAME = None
|
|
|
+ STORAGE = KeyBlobStorage.NO_STORAGE
|
|
|
+
|
|
|
iterations = 100000 # must not be changed ever!
|
|
|
|
|
|
@classmethod
|
|
@@ -623,6 +628,9 @@ class KeyfileKeyBase(AESKeyBase):
|
|
|
class KeyfileKey(ID_HMAC_SHA_256, KeyfileKeyBase):
|
|
|
TYPE = 0x00
|
|
|
NAME = 'key file'
|
|
|
+ ARG_NAME = 'keyfile'
|
|
|
+ STORAGE = KeyBlobStorage.KEYFILE
|
|
|
+
|
|
|
FILE_ID = 'BORG_KEY'
|
|
|
|
|
|
def sanity_check(self, filename, id):
|
|
@@ -683,6 +691,8 @@ class KeyfileKey(ID_HMAC_SHA_256, KeyfileKeyBase):
|
|
|
class RepoKey(ID_HMAC_SHA_256, KeyfileKeyBase):
|
|
|
TYPE = 0x03
|
|
|
NAME = 'repokey'
|
|
|
+ ARG_NAME = 'repokey'
|
|
|
+ STORAGE = KeyBlobStorage.REPO
|
|
|
|
|
|
def find_key(self):
|
|
|
loc = self.repository._location.canonical_path()
|
|
@@ -715,6 +725,9 @@ class RepoKey(ID_HMAC_SHA_256, KeyfileKeyBase):
|
|
|
class Blake2KeyfileKey(ID_BLAKE2b_256, KeyfileKey):
|
|
|
TYPE = 0x04
|
|
|
NAME = 'key file BLAKE2b'
|
|
|
+ ARG_NAME = 'keyfile-blake2'
|
|
|
+ STORAGE = KeyBlobStorage.KEYFILE
|
|
|
+
|
|
|
FILE_ID = 'BORG_KEY'
|
|
|
MAC = blake2b_256
|
|
|
|
|
@@ -722,12 +735,17 @@ class Blake2KeyfileKey(ID_BLAKE2b_256, KeyfileKey):
|
|
|
class Blake2RepoKey(ID_BLAKE2b_256, RepoKey):
|
|
|
TYPE = 0x05
|
|
|
NAME = 'repokey BLAKE2b'
|
|
|
+ ARG_NAME = 'repokey-blake2'
|
|
|
+ STORAGE = KeyBlobStorage.REPO
|
|
|
+
|
|
|
MAC = blake2b_256
|
|
|
|
|
|
|
|
|
class AuthenticatedKey(ID_BLAKE2b_256, RepoKey):
|
|
|
TYPE = 0x06
|
|
|
NAME = 'authenticated BLAKE2b'
|
|
|
+ ARG_NAME = 'authenticated'
|
|
|
+ STORAGE = KeyBlobStorage.REPO
|
|
|
|
|
|
def encrypt(self, chunk):
|
|
|
chunk = self.compress(chunk)
|
|
@@ -742,3 +760,11 @@ class AuthenticatedKey(ID_BLAKE2b_256, RepoKey):
|
|
|
data = self.compressor.decompress(payload)
|
|
|
self.assert_id(id, data)
|
|
|
return Chunk(data)
|
|
|
+
|
|
|
+
|
|
|
+AVAILABLE_KEY_TYPES = (
|
|
|
+ PlaintextKey,
|
|
|
+ PassphraseKey,
|
|
|
+ KeyfileKey, RepoKey,
|
|
|
+ Blake2KeyfileKey, Blake2RepoKey, AuthenticatedKey,
|
|
|
+)
|