1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2024-12-25 09:19:31 +00:00

Redo key_creator, key_factory, centralise key knowledge (#2272)

* key: put key metadata (name, storage) into key classses

* keymanager: use key-declared storage types
This commit is contained in:
enkore 2017-03-08 17:08:54 +01:00 committed by GitHub
parent 1f861ec78e
commit fc41c98a86
2 changed files with 62 additions and 45 deletions

View file

@ -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 @@ def unpack_and_verify_manifest(self, data, force_tam_not_required=False):
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 @@ def get_new_target(self, args):
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 @@ def save(self, target, passphrase):
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 @@ def save(self, target, passphrase):
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 @@ def decrypt(self, id, data, decompress=True):
data = self.compressor.decompress(payload)
self.assert_id(id, data)
return Chunk(data)
AVAILABLE_KEY_TYPES = (
PlaintextKey,
PassphraseKey,
KeyfileKey, RepoKey,
Blake2KeyfileKey, Blake2RepoKey, AuthenticatedKey,
)

View file

@ -4,7 +4,7 @@
from hashlib import sha256
import pkgutil
from .key import KeyfileKey, RepoKey, PassphraseKey, KeyfileNotFoundError, PlaintextKey
from .key import KeyfileKey, KeyfileNotFoundError, KeyBlobStorage, identify_key
from .helpers import Manifest, NoManifestError, Error, yes, bin_to_hex
from .repository import Repository
@ -31,10 +31,6 @@ def sha256_truncated(data, num):
return h.hexdigest()[:num]
KEYBLOB_LOCAL = 'local'
KEYBLOB_REPO = 'repo'
class KeyManager:
def __init__(self, repository):
self.repository = repository
@ -42,32 +38,27 @@ def __init__(self, repository):
self.keyblob_storage = None
try:
cdata = self.repository.get(Manifest.MANIFEST_ID)
manifest_data = self.repository.get(Manifest.MANIFEST_ID)
except Repository.ObjectNotFound:
raise NoManifestError
key_type = cdata[0]
if key_type == KeyfileKey.TYPE:
self.keyblob_storage = KEYBLOB_LOCAL
elif key_type == RepoKey.TYPE or key_type == PassphraseKey.TYPE:
self.keyblob_storage = KEYBLOB_REPO
elif key_type == PlaintextKey.TYPE:
key = identify_key(manifest_data)
self.keyblob_storage = key.STORAGE
if self.keyblob_storage == KeyBlobStorage.NO_STORAGE:
raise UnencryptedRepo()
else:
raise UnknownKeyType(key_type)
def load_keyblob(self):
if self.keyblob_storage == KEYBLOB_LOCAL:
if self.keyblob_storage == KeyBlobStorage.KEYFILE:
k = KeyfileKey(self.repository)
target = k.find_key()
with open(target, 'r') as fd:
self.keyblob = ''.join(fd.readlines()[1:])
elif self.keyblob_storage == KEYBLOB_REPO:
elif self.keyblob_storage == KeyBlobStorage.REPO:
self.keyblob = self.repository.load_key().decode()
def store_keyblob(self, args):
if self.keyblob_storage == KEYBLOB_LOCAL:
if self.keyblob_storage == KeyBlobStorage.KEYFILE:
k = KeyfileKey(self.repository)
try:
target = k.find_key()
@ -75,7 +66,7 @@ def store_keyblob(self, args):
target = k.get_new_target(args)
self.store_keyfile(target)
elif self.keyblob_storage == KEYBLOB_REPO:
elif self.keyblob_storage == KeyBlobStorage.REPO:
self.repository.save_key(self.keyblob.encode('utf-8'))
def get_keyfile_data(self):