diff --git a/darc/archiver.py b/darc/archiver.py index a0256ca4b..4c5eba1a7 100644 --- a/darc/archiver.py +++ b/darc/archiver.py @@ -42,15 +42,24 @@ class Archiver(object): return StoreServer().serve() def do_init(self, args): + print 'Initializing store "%s"' % args.store.orig store = self.open_store(args.store, create=True) - Key.create(store, args.store.to_key_filename(), - password=args.password) - key = Key(store) + key = Key.create(store, args.store.to_key_filename(), password=args.password) + print 'Key file "%s" created.' % key.path + print 'Remember that this file (and password) is needed to access your data. Keep it safe!' + print manifest = Manifest(store, key, dont_load=True) manifest.write() store.commit() return self.exit_code + def do_chpasswd(self, args): + key = Key() + key.open(args.store.to_key_filename()) + key.chpasswd() + print 'Key file "%s" updated' % key.path + return self.exit_code + def do_create(self, args): t0 = datetime.now() store = self.open_store(args.archive) @@ -300,6 +309,12 @@ class Archiver(object): type=location_validator(archive=False), help='Store to create') + subparser = subparsers.add_parser('change-password', parents=[common_parser]) + subparser.set_defaults(func=self.do_chpasswd) + subparser.add_argument('store', metavar='STORE', + type=location_validator(archive=False), + help='Key file to operate on') + subparser = subparsers.add_parser('create', parents=[common_parser]) subparser.set_defaults(func=self.do_create) subparser.add_argument('-s', '--stats', dest='stats', diff --git a/darc/helpers.py b/darc/helpers.py index a0aa35eb8..574959128 100644 --- a/darc/helpers.py +++ b/darc/helpers.py @@ -13,6 +13,7 @@ import sys import time import urllib + class Manifest(object): MANIFEST_ID = '\0' * 32 @@ -327,6 +328,7 @@ class Location(object): r'(?P[^:]*)(?:::(?P.+))?') def __init__(self, text): + self.orig = text if not self.parse(text): raise ValueError diff --git a/darc/key.py b/darc/key.py index 2285fcc6f..e3e2dbbc2 100644 --- a/darc/key.py +++ b/darc/key.py @@ -19,9 +19,9 @@ PREFIX = '\0' * 8 class Key(object): FILE_ID = 'DARC KEY' - def __init__(self, store=None): + def __init__(self, store=None, password=None): if store: - self.open(self.find_key_file(store)) + self.open(self.find_key_file(store), password=password) def find_key_file(self, store): id = store.id.encode('hex') @@ -34,17 +34,18 @@ class Key(object): return filename raise Exception('Key file for store with ID %s not found' % id) - def open(self, filename): + def open(self, filename, prompt=None, password=None): + prompt = prompt or 'Enter password for %s: ' % filename with open(filename, 'rb') as fd: lines = fd.readlines() if not lines[0].startswith(self.FILE_ID) != self.FILE_ID: raise ValueError('Not a DARC key file') self.store_id = lines[0][len(self.FILE_ID):].strip().decode('hex') cdata = (''.join(lines[1:])).decode('base64') - self.password = '' - data = self.decrypt_key_file(cdata, '') + self.password = password or '' + data = self.decrypt_key_file(cdata, self.password) while not data: - self.password = getpass('Key password: ') + self.password = getpass(prompt) if not self.password: raise Exception('Key decryption failed') data = self.decrypt_key_file(cdata, self.password) @@ -59,6 +60,7 @@ class Key(object): self.id_key = key['id_key'] self.chunk_seed = key['chunk_seed'] self.counter = Counter.new(64, initial_value=1, prefix=PREFIX) + self.path = filename def post_manifest_load(self, config): iv = bytes_to_long(config['aes_counter'])+100 @@ -69,7 +71,7 @@ class Key(object): def encrypt_key_file(self, data, password): salt = get_random_bytes(32) - iterations = 2000 + iterations = 10000 key = pbkdf2(password, salt, 32, iterations, hashlib.sha256) hash = HMAC.new(key, data, SHA256).digest() cdata = AES.new(key, AES.MODE_CTR, counter=Counter.new(128)).encrypt(data) @@ -106,9 +108,9 @@ class Key(object): with open(path, 'wb') as fd: fd.write('%s %s\n' % (self.FILE_ID, self.store_id.encode('hex'))) fd.write(data.encode('base64')) - print 'Key file "%s" created' % path + self.path = path - def chpass(self): + def chpasswd(self): password, password2 = 1, 2 while password != password2: password = getpass('New password: ') @@ -130,8 +132,8 @@ class Key(object): else: password, password2 = 1, 2 while password != password2: - password = getpass('Key password: ') - password2 = getpass('Key password again: ') + password = getpass('Key file password (Leave blank for no password): ') + password2 = getpass('Key file password again: ') if password != password2: print 'Passwords do not match' key = Key() @@ -148,7 +150,7 @@ class Key(object): if key.chunk_seed & 0x80000000: key.chunk_seed = key.chunk_seed - 0xffffffff - 1 key.save(path, password) - return 0 + return Key(store, password=password) def id_hash(self, data): """Return HMAC hash using the "id" HMAC key