diff --git a/darc/archive.py b/darc/archive.py index 2e80a8b38..67e322735 100644 --- a/darc/archive.py +++ b/darc/archive.py @@ -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 diff --git a/darc/archiver.py b/darc/archiver.py index 8e944214e..159b009cc 100644 --- a/darc/archiver.py +++ b/darc/archiver.py @@ -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('~'), diff --git a/darc/cache.py b/darc/cache.py index 436da49ef..8fe0825c9 100644 --- a/darc/cache.py +++ b/darc/cache.py @@ -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) diff --git a/darc/crypto.py b/darc/keychain.py similarity index 91% rename from darc/crypto.py rename to darc/keychain.py index 5560431ac..b525b7bd8 100644 --- a/darc/crypto.py +++ b/darc/keychain.py @@ -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)) + +