mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-09 08:51:54 -04:00
Added "change-password" subcommand
This commit is contained in:
parent
9e5b13df44
commit
f282fd6930
3 changed files with 34 additions and 15 deletions
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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<path>[^:]*)(?:::(?P<archive>.+))?')
|
||||
|
||||
def __init__(self, text):
|
||||
self.orig = text
|
||||
if not self.parse(text):
|
||||
raise ValueError
|
||||
|
||||
|
|
|
|||
26
darc/key.py
26
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
|
||||
|
|
|
|||
Loading…
Reference in a new issue