From 49744c0457ee8ee3d2797671c28ed6524da7e4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Borgstr=C3=B6m?= Date: Tue, 18 Dec 2012 21:58:58 +0100 Subject: [PATCH] Improved AES counter storage --- darc/helpers.py | 2 -- darc/key.py | 62 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/darc/helpers.py b/darc/helpers.py index 1ed62bb97..ff824b1c7 100644 --- a/darc/helpers.py +++ b/darc/helpers.py @@ -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, diff --git a/darc/key.py b/darc/key.py index a50ed070c..1a2ba18b6 100644 --- a/darc/key.py +++ b/darc/key.py @@ -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():