Sfoglia il codice sorgente

Improved AES counter storage

Jonas Borgström 12 anni fa
parent
commit
49744c0457
2 ha cambiato i file con 42 aggiunte e 22 eliminazioni
  1. 0 2
      darc/helpers.py
  2. 42 20
      darc/key.py

+ 0 - 2
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,

+ 42 - 20
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():