1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2025-02-24 07:01:59 +00:00

Merge CryptoManager and KeyChain into Keychain

This commit is contained in:
Jonas Borgström 2010-10-31 22:21:59 +01:00
parent 869f720be4
commit d70993fc83
4 changed files with 64 additions and 73 deletions

View file

@ -21,32 +21,32 @@ class Archive(object):
class DoesNotExist(Exception):
pass
def __init__(self, store, crypto, name=None):
self.crypto = crypto
def __init__(self, store, keychain, name=None):
self.keychain = keychain
self.store = store
self.items = []
self.chunks = []
self.chunk_idx = {}
self.hard_links = {}
if name:
self.load(self.crypto.id_hash(name))
self.load(self.keychain.id_hash(name))
def load(self, id):
self.id = id
try:
data, self.hash = self.crypto.decrypt(self.store.get(NS_ARCHIVE_METADATA, self.id))
data, self.hash = self.keychain.decrypt(self.store.get(NS_ARCHIVE_METADATA, self.id))
except self.store.DoesNotExist:
raise self.DoesNotExist
self.metadata = msgpack.unpackb(data)
assert self.metadata['version'] == 1
def get_items(self):
data, chunks_hash = self.crypto.decrypt(self.store.get(NS_ARCHIVE_CHUNKS, self.id))
data, chunks_hash = self.keychain.decrypt(self.store.get(NS_ARCHIVE_CHUNKS, self.id))
chunks = msgpack.unpackb(data)
assert chunks['version'] == 1
assert self.metadata['chunks_hash'] == chunks_hash
self.chunks = chunks['chunks']
data, items_hash = self.crypto.decrypt(self.store.get(NS_ARCHIVE_ITEMS, self.id))
data, items_hash = self.keychain.decrypt(self.store.get(NS_ARCHIVE_ITEMS, self.id))
items = msgpack.unpackb(data)
assert items['version'] == 1
assert self.metadata['items_hash'] == items_hash
@ -55,12 +55,12 @@ def get_items(self):
self.chunk_idx[i] = chunk[0]
def save(self, name):
self.id = self.crypto.id_hash(name)
self.id = self.keychain.id_hash(name)
chunks = {'version': 1, 'chunks': self.chunks}
data, chunks_hash = self.crypto.encrypt_create(msgpack.packb(chunks))
data, chunks_hash = self.keychain.encrypt_create(msgpack.packb(chunks))
self.store.put(NS_ARCHIVE_CHUNKS, self.id, data)
items = {'version': 1, 'items': self.items}
data, items_hash = self.crypto.encrypt_read(msgpack.packb(items))
data, items_hash = self.keychain.encrypt_read(msgpack.packb(items))
self.store.put(NS_ARCHIVE_ITEMS, self.id, data)
metadata = {
'version': 1,
@ -72,7 +72,7 @@ def save(self, name):
'username': getuser(),
'time': datetime.utcnow().isoformat(),
}
data, self.hash = self.crypto.encrypt_read(msgpack.packb(metadata))
data, self.hash = self.keychain.encrypt_read(msgpack.packb(metadata))
self.store.put(NS_ARCHIVE_METADATA, self.id, data)
self.store.commit()
@ -134,8 +134,8 @@ def extract_item(self, item, dest=None):
for chunk in item['chunks']:
id = self.chunk_idx[chunk]
try:
data, hash = self.crypto.decrypt(self.store.get(NS_CHUNK, id))
if self.crypto.id_hash(data) != id:
data, hash = self.keychain.decrypt(self.store.get(NS_CHUNK, id))
if self.keychain.id_hash(data) != id:
raise IntegrityError('chunk id did not match')
fd.write(data)
except ValueError:
@ -171,8 +171,8 @@ def verify_file(self, item):
for chunk in item['chunks']:
id = self.chunk_idx[chunk]
try:
data, hash = self.crypto.decrypt(self.store.get(NS_CHUNK, id))
if self.crypto.id_hash(data) != id:
data, hash = self.keychain.decrypt(self.store.get(NS_CHUNK, id))
if self.keychain.id_hash(data) != id:
raise IntegrityError('chunk id did not match')
except IntegrityError:
return False
@ -227,7 +227,7 @@ def process_file(self, path, st, cache):
return
else:
self.hard_links[st.st_ino, st.st_dev] = safe_path
path_hash = self.crypto.id_hash(path.encode('utf-8'))
path_hash = self.keychain.id_hash(path.encode('utf-8'))
ids, size = cache.file_known_and_unchanged(path_hash, st)
if ids is not None:
# Make sure all ids are available
@ -245,7 +245,7 @@ def process_file(self, path, st, cache):
ids = []
chunks = []
for chunk in chunkify(fd, CHUNK_SIZE, 30):
id = self.crypto.id_hash(chunk)
id = self.keychain.id_hash(chunk)
ids.append(id)
try:
chunks.append(self.chunk_idx[id])
@ -275,8 +275,8 @@ def process_chunk(self, id, data, cache):
return idx
@staticmethod
def list_archives(store, crypto):
def list_archives(store, keychain):
for id in list(store.list(NS_ARCHIVE_METADATA)):
archive = Archive(store, crypto)
archive = Archive(store, keychain)
archive.load(id)
yield archive

View file

@ -7,7 +7,7 @@
from .archive import Archive
from .store import Store
from .cache import Cache
from .crypto import CryptoManager, KeyChain
from .keychain import Keychain
from .helpers import location_validator, format_file_size, format_time, format_file_mode, walk_dir
@ -38,17 +38,16 @@ def do_init(self, args):
def do_create(self, args):
store = self.open_store(args.archive)
keychain = KeyChain(args.keychain)
crypto = CryptoManager(keychain)
keychain = Keychain(args.keychain)
try:
Archive(store, crypto, args.archive.archive)
Archive(store, keychain, args.archive.archive)
except Archive.DoesNotExist:
pass
else:
self.print_error('Archive already exists')
return self.exit_code
archive = Archive(store, crypto)
cache = Cache(store, archive.crypto)
archive = Archive(store, keychain)
cache = Cache(store, keychain)
for path in args.paths:
for path, st in walk_dir(unicode(path)):
if stat.S_ISDIR(st.st_mode):
@ -70,9 +69,8 @@ def do_create(self, args):
def do_extract(self, args):
store = self.open_store(args.archive)
keychain = KeyChain(args.keychain)
crypto = CryptoManager(keychain)
archive = Archive(store, crypto, args.archive.archive)
keychain = Keychain(args.keychain)
archive = Archive(store, keychain, args.archive.archive)
archive.get_items()
dirs = []
for item in archive.items:
@ -89,20 +87,18 @@ def do_extract(self, args):
def do_delete(self, args):
store = self.open_store(args.archive)
keychain = KeyChain(args.keychain)
crypto = CryptoManager(keychain)
archive = Archive(store, crypto, args.archive.archive)
cache = Cache(store, archive.crypto)
keychain = Keychain(args.keychain)
archive = Archive(store, keychain, args.archive.archive)
cache = Cache(store, keychain)
archive.delete(cache)
return self.exit_code
def do_list(self, args):
store = self.open_store(args.src)
keychain = KeyChain(args.keychain)
crypto = CryptoManager(keychain)
keychain = Keychain(args.keychain)
if args.src.archive:
tmap = {1: 'p', 2: 'c', 4: 'd', 6: 'b', 010: '-', 012: 'l', 014: 's'}
archive = Archive(store, crypto, args.src.archive)
archive = Archive(store, keychain, args.src.archive)
archive.get_items()
for item in archive.items:
type = tmap.get(item['mode'] / 4096, '?')
@ -112,15 +108,14 @@ def do_list(self, args):
print '%s%s %-6s %-6s %8d %s %s' % (type, mode, item['user'],
item['group'], size, mtime, item['path'])
else:
for archive in Archive.list_archives(store, crypto):
for archive in Archive.list_archives(store, keychain):
print '%(name)-20s %(time)s' % archive.metadata
return self.exit_code
def do_verify(self, args):
store = self.open_store(args.archive)
keychain = KeyChain(args.keychain)
crypto = CryptoManager(keychain)
archive = Archive(store, crypto, args.archive.archive)
keychain = Keychain(args.keychain)
archive = Archive(store, keychain, args.archive.archive)
archive.get_items()
for item in archive.items:
if stat.S_ISREG(item['mode']) and not 'source' in item:
@ -134,10 +129,9 @@ def do_verify(self, args):
def do_info(self, args):
store = self.open_store(args.archive)
keychain = KeyChain(args.keychain)
crypto = CryptoManager(keychain)
archive = Archive(store, crypto, args.archive.archive)
cache = Cache(store, archive.crypto)
keychain = Keychain(args.keychain)
archive = Archive(store, keychain, args.archive.archive)
cache = Cache(store, keychain)
osize, csize, usize = archive.stats(cache)
print 'Name:', archive.metadata['name']
print 'Hostname:', archive.metadata['hostname']
@ -151,15 +145,15 @@ def do_info(self, args):
return self.exit_code
def do_init_keychain(self, args):
return KeyChain.generate(args.keychain)
return Keychain.generate(args.keychain)
def do_export_restricted(self, args):
keychain = KeyChain(args.keychain)
keychain = Keychain(args.keychain)
keychain.restrict(args.output)
return self.exit_code
def do_keychain_chpass(self, args):
return KeyChain(args.keychain).chpass()
return Keychain(args.keychain).chpass()
def run(self, args=None):
default_keychain = os.path.join(os.path.expanduser('~'),

View file

@ -8,9 +8,9 @@ class Cache(object):
"""Client Side cache
"""
def __init__(self, store, crypto):
def __init__(self, store, keychain):
self.store = store
self.crypto = crypto
self.keychain = keychain
self.path = os.path.join(os.path.expanduser('~'), '.darc', 'cache',
'%s.cache' % self.store.id.encode('hex'))
self.tid = -1
@ -22,7 +22,7 @@ def open(self):
if not os.path.exists(self.path):
return
with open(self.path, 'rb') as fd:
data, hash = self.crypto.decrypt(fd.read())
data, hash = self.keychain.decrypt(fd.read())
cache = msgpack.unpackb(data)
assert cache['version'] == 1
self.chunk_counts = cache['chunk_counts']
@ -39,7 +39,7 @@ def init(self):
if self.store.tid == 0:
return
for id in list(self.store.list(NS_ARCHIVE_CHUNKS)):
data, hash = self.crypto.decrypt(self.store.get(NS_ARCHIVE_CHUNKS, id))
data, hash = self.keychain.decrypt(self.store.get(NS_ARCHIVE_CHUNKS, id))
cindex = msgpack.unpackb(data)
for id, size in cindex['chunks']:
try:
@ -61,7 +61,7 @@ def save(self):
'chunk_counts': self.chunk_counts,
'file_chunks': dict(self.filter_file_chunks()),
}
data, hash = self.crypto.encrypt_create(msgpack.packb(cache))
data, hash = self.keychain.encrypt_create(msgpack.packb(cache))
cachedir = os.path.dirname(self.path)
if not os.path.exists(cachedir):
os.makedirs(cachedir)
@ -71,7 +71,7 @@ def save(self):
def add_chunk(self, id, data):
if self.seen_chunk(id):
return self.chunk_incref(id)
data, hash = self.crypto.encrypt_read(data)
data, hash = self.keychain.encrypt_read(data)
csize = len(data)
self.store.put(NS_CHUNK, id, data)
self.chunk_counts[id] = (1, csize)

View file

@ -15,10 +15,16 @@
from .oaep import OAEP
class KeyChain(object):
class Keychain(object):
FILE_ID = 'DARC KEYCHAIN'
CREATE = '\1'
READ = '\2'
def __init__(self, path=None):
self._key_cache = {}
self.read_key = os.urandom(32)
self.create_key = os.urandom(32)
self.aes_id = self.rsa_read = self.rsa_create = None
self.path = path
if path:
@ -31,7 +37,7 @@ def open(self, path):
raise ValueError('Not a keychain')
cdata = fd.read()
self.password = ''
data = self.decrypt(cdata, '')
data = self._decrypt(cdata, '')
while not data:
self.password = getpass('Keychain password: ')
if not self.password:
@ -44,6 +50,10 @@ def open(self, path):
self.aes_id = chain['aes_id']
self.rsa_read = RSA.importKey(chain['rsa_read'])
self.rsa_create = RSA.importKey(chain['rsa_create'])
self.read_encrypted = OAEP(256, hash=SHA256).encode(self.read_key, os.urandom(32))
self.read_encrypted = self.rsa_read.encrypt(self.read_encrypted, '')[0]
self.create_encrypted = OAEP(256, hash=SHA256).encode(self.create_key, os.urandom(32))
self.create_encrypted = self.rsa_create.encrypt(self.create_encrypted, '')[0]
def encrypt(self, data, password):
salt = os.urandom(32)
@ -61,7 +71,7 @@ def encrypt(self, data, password):
}
return msgpack.packb(d)
def decrypt(self, data, password):
def _decrypt(self, data, password):
d = msgpack.unpackb(data)
assert d['version'] == 1
assert d['algorithm'] == 'SHA256'
@ -113,7 +123,7 @@ def generate(path):
password2 = getpass('Keychain password again: ')
if password != password2:
print 'Passwords do not match'
chain = KeyChain()
chain = Keychain()
print 'Generating keychain'
chain.aes_id = os.urandom(32)
chain.rsa_read = RSA.generate(2048)
@ -121,23 +131,8 @@ def generate(path):
chain.save(path, password)
return 0
class CryptoManager(object):
CREATE = '\1'
READ = '\2'
def __init__(self, keychain):
self._key_cache = {}
self.keychain = keychain
self.read_key = os.urandom(32)
self.create_key = os.urandom(32)
self.read_encrypted = OAEP(256, hash=SHA256).encode(self.read_key, os.urandom(32))
self.read_encrypted = keychain.rsa_read.encrypt(self.read_encrypted, '')[0]
self.create_encrypted = OAEP(256, hash=SHA256).encode(self.create_key, os.urandom(32))
self.create_encrypted = keychain.rsa_create.encrypt(self.create_encrypted, '')[0]
def id_hash(self, data):
return HMAC.new(self.keychain.aes_id, data, SHA256).digest()
return HMAC.new(self.aes_id, data, SHA256).digest()
def encrypt_read(self, data):
data = zlib.compress(data)
@ -163,7 +158,7 @@ def decrypt_key(self, data, rsa_key):
def decrypt(self, data):
type = data[0]
if type == self.READ:
key = self.decrypt_key(data[1:257], self.keychain.rsa_read)
key = self.decrypt_key(data[1:257], self.rsa_read)
hash = data[257:289]
counter = Counter.new(128, initial_value=bytes_to_long(hash[:16]), allow_wraparound=True)
data = AES.new(key, AES.MODE_CTR, counter=counter).decrypt(data[289:])
@ -171,7 +166,7 @@ def decrypt(self, data):
raise IntegrityError('decryption failed')
return zlib.decompress(data), hash
elif type == self.CREATE:
key = self.decrypt_key(data[1:257], self.keychain.rsa_create)
key = self.decrypt_key(data[1:257], self.rsa_create)
hash = data[257:289]
counter = Counter.new(128, initial_value=bytes_to_long(hash[:16]), allow_wraparound=True)
data = AES.new(key, AES.MODE_CTR, '', counter=counter).decrypt(data[289:])
@ -181,3 +176,5 @@ def decrypt(self, data):
else:
raise Exception('Unknown pack type %d found' % ord(type))