1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2025-03-19 18:36:07 +00:00

Improved AES counter storage

This commit is contained in:
Jonas Borgström 2012-12-18 21:58:58 +01:00
parent 5b28d428b7
commit 49744c0457
2 changed files with 42 additions and 22 deletions

View file

@ -36,11 +36,9 @@ class Manifest(object):
raise ValueError('Invalid manifest version')
manifest.archives = m['archives']
manifest.config = m['config']
key.post_manifest_load(manifest.config)
return manifest, key
def write(self):
self.key.pre_manifest_write(self)
data = msgpack.packb({
'version': 1,
'archives': self.archives,

View file

@ -59,12 +59,6 @@ class KeyBase(object):
def decrypt(self, id, data):
pass
def post_manifest_load(self, config):
pass
def pre_manifest_write(self, manifest):
pass
class PlaintextKey(KeyBase):
TYPE = PLAINTEXT
@ -97,13 +91,6 @@ class PlaintextKey(KeyBase):
class AESKeyBase(KeyBase):
def post_manifest_load(self, config):
iv = bytes_to_long(config['aes_counter']) + 100
self.counter = Counter.new(64, initial_value=iv, prefix=PREFIX)
def pre_manifest_write(self, manifest):
manifest.config['aes_counter'] = long_to_bytes(self.counter.next_value(), 8)
def id_hash(self, data):
"""Return HMAC hash using the "id" HMAC key
"""
@ -130,6 +117,12 @@ class AESKeyBase(KeyBase):
raise IntegrityError('Chunk id verification failed')
return data
def extract_iv(self, payload):
if payload[0] != self.TYPE:
raise IntegrityError('Invalid encryption envelope')
nonce = bytes_to_long(payload[33:41])
return nonce
def init_from_random_data(self, data):
self.enc_key = data[0:32]
self.enc_hmac_key = data[32:64]
@ -177,6 +170,8 @@ class PassphraseKey(AESKeyBase):
key.init(store, passphrase)
try:
key.decrypt(None, manifest_data)
iv = key.extract_iv(manifest_data)
key.counter = Counter.new(64, initial_value=iv + 1000, prefix=PREFIX)
return key
except IntegrityError:
passphrase = getpass(prompt)
@ -197,6 +192,8 @@ class KeyfileKey(AESKeyBase):
passphrase = os.environ.get('DARC_PASSPHRASE', '')
while not key.load(path, passphrase):
passphrase = getpass(prompt)
iv = key.extract_iv(manifest_data)
key.counter = Counter.new(64, initial_value=iv + 1000, prefix=PREFIX)
return key
@classmethod
@ -260,7 +257,7 @@ class KeyfileKey(AESKeyBase):
'store_id': self.store_id,
'enc_key': self.enc_key,
'enc_hmac_key': self.enc_hmac_key,
'id_key': self.enc_key,
'id_key': self.id_key,
'chunk_seed': self.chunk_seed,
}
data = self.encrypt_key_file(msgpack.packb(key), passphrase)
@ -308,14 +305,23 @@ class KeyfileKey(AESKeyBase):
class KeyTestCase(unittest.TestCase):
def setUp(self):
self.tmppath = tempfile.mkdtemp()
os.environ['DARC_KEYS_DIR'] = self.tmppath
def tearDown(self):
shutil.rmtree(self.tmppath)
class MockStore(object):
class _Location(object):
orig = '/some/place'
_location = _Location()
id = '\0' * 32
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
self.keys_path = os.path.join(self.tmpdir, 'keys')
os.mkdir(self.keys_path)
os.environ['DARC_KEYS_DIR'] = self.keys_path
os.environ['DARC_KEYS_DIR'] = self.tmpdir
def tearDown(self):
shutil.rmtree(self.tmpdir)
@ -331,20 +337,36 @@ class KeyTestCase(unittest.TestCase):
store = Location(tempfile.mkstemp()[1])
os.environ['DARC_PASSPHRASE'] = 'test'
key = KeyfileKey.create(self.MockStore(), MockArgs())
key = KeyfileKey.detect(self.MockStore(), None)
self.assertEqual(bytes_to_long(key.counter()), 1)
manifest = key.encrypt('')
iv = key.extract_iv(manifest)
key2 = KeyfileKey.detect(self.MockStore(), manifest)
self.assertEqual(bytes_to_long(key2.counter()), iv + 1000)
# Key data sanity check
self.assertEqual(len(set([key2.id_key, key2.enc_key, key2.enc_hmac_key])), 3)
self.assertEqual(key2.chunk_seed == 0, False)
data = 'foo'
self.assertEqual(data, key.decrypt(key.id_hash(data), key.encrypt(data)))
self.assertEqual(data, key2.decrypt(key.id_hash(data), key.encrypt(data)))
def test_passphrase(self):
os.environ['DARC_PASSPHRASE'] = 'test'
key = PassphraseKey.create(self.MockStore(), None)
self.assertEqual(bytes_to_long(key.counter()), 1)
self.assertEqual(key.id_key.encode('hex'), 'f28e915da78a972786da47fee6c4bd2960a421b9bdbdb35a7942eb82552e9a72')
self.assertEqual(key.enc_hmac_key.encode('hex'), '169c6082f209e524ea97e2c75318936f6e93c101b9345942a95491e9ae1738ca')
self.assertEqual(key.enc_key.encode('hex'), 'c05dd423843d4dd32a52e4dc07bb11acabe215917fc5cf3a3df6c92b47af79ba')
self.assertEqual(key.chunk_seed, -324662077)
manifest = key.encrypt('')
iv = key.extract_iv(manifest)
key2 = PassphraseKey.detect(self.MockStore(), manifest)
self.assertEqual(bytes_to_long(key2.counter()), iv + 1000)
self.assertEqual(key.id_key, key2.id_key)
self.assertEqual(key.enc_hmac_key, key2.enc_hmac_key)
self.assertEqual(key.enc_key, key2.enc_key)
self.assertEqual(key.chunk_seed, key2.chunk_seed)
data = 'foo'
self.assertEqual(key.id_hash(data).encode('hex'), '016c27cd40dc8e84f196f3b43a9424e8472897e09f6935d0d3a82fb41664bad7')
self.assertEqual(data, key.decrypt(key.id_hash(data), key.encrypt(data)))
self.assertEqual(data, key2.decrypt(key2.id_hash(data), key.encrypt(data)))
def suite():