浏览代码

Added keychain encryption

Jonas Borgström 14 年之前
父节点
当前提交
25c302214f
共有 3 个文件被更改,包括 57 次插入8 次删除
  1. 3 2
      dedupestore/archive.py
  2. 9 2
      dedupestore/archiver.py
  3. 45 4
      dedupestore/crypto.py

+ 3 - 2
dedupestore/archive.py

@@ -59,7 +59,7 @@ class Archive(object):
             'ts': datetime.utcnow().isoformat(),
             'items': self.items,
         }
-        data, hash = self.crypto.encrypt_read(msgpack.packb(archive))
+        data, self.hash = self.crypto.encrypt_read(msgpack.packb(archive))
         self.store.put(NS_ARCHIVES, self.id, data)
         self.store.commit()
 
@@ -181,8 +181,9 @@ class Archive(object):
                     yield x
 
     def create(self, name, paths, cache):
+        id = self.crypto.id_hash(name)
         try:
-            self.store.get(NS_ARCHIVES, name)
+            self.store.get(NS_ARCHIVES, id)
         except self.store.DoesNotExist:
             pass
         else:

+ 9 - 2
dedupestore/archiver.py

@@ -1,4 +1,5 @@
 import argparse
+from getpass import getpass
 import logging
 import sys
 
@@ -77,8 +78,14 @@ class Archiver(object):
         return self.exit_code_from_logger()
 
     def do_keychain_generate(self, args):
-        keychain = KeyChain.generate()
-        keychain.save(args.path)
+        password = ''
+        password2 = 'x'
+        while password != password2:
+            password = getpass('Keychain password: ')
+            password2 = getpass('Keychain password again: ')
+            if password != password2:
+                logging.error('Passwords do not match')
+        keychain = KeyChain.generate(args.path, password)
         return 0
 
     def run(self, args=None):

+ 45 - 4
dedupestore/crypto.py

@@ -1,8 +1,11 @@
+from getpass import getpass
+import hashlib
 import os
 import logging
 import msgpack
 import zlib
 
+from pbkdf2 import pbkdf2
 from Crypto.Cipher import AES
 from Crypto.Hash import SHA256, HMAC
 from Crypto.PublicKey import RSA
@@ -22,30 +25,68 @@ class KeyChain(object):
 
     def open(self, path):
         with open(path, 'rb') as fd:
-            chain = msgpack.unpackb(fd.read())
+            cdata = fd.read()
+        data = self.decrypt(cdata, '')
+        while not data:
+            password = getpass('Keychain password: ')
+            if not password:
+                raise Exception('Keychain decryption failed')
+            data = self.decrypt(cdata, password)
+            if not data:
+                logging.error('Incorrect password')
+        chain = msgpack.unpackb(data)
         logging.info('Key chain "%s" opened', path)
         assert chain['version'] == 1
         self.aes_id = chain['aes_id']
         self.rsa_read = RSA.importKey(chain['rsa_read'])
         self.rsa_create = RSA.importKey(chain['rsa_create'])
 
-    def save(self, path):
+    def encrypt(self, data, password):
+        salt = os.urandom(32)
+        iterations = 2000
+        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)
+        d = {
+            'version': 1,
+            'salt': salt,
+            'iterations': iterations,
+            'algorithm': 'SHA256',
+            'hash': hash,
+            'data': cdata,
+        }
+        return msgpack.packb(d)
+
+    def decrypt(self, data, password):
+        d = msgpack.unpackb(data)
+        assert d['version'] == 1
+        assert d['algorithm'] == 'SHA256'
+        key = pbkdf2(password, d['salt'], 32, d['iterations'], hashlib.sha256)
+        data = AES.new(key, AES.MODE_CTR, counter=Counter.new(128)).decrypt(d['data'])
+        if HMAC.new(key, data, SHA256).digest() != d['hash']:
+            return None
+        return data
+
+    def save(self, path, password):
         chain = {
             'version': 1,
             'aes_id': self.aes_id,
             'rsa_read': self.rsa_read.exportKey('PEM'),
             'rsa_create': self.rsa_create.exportKey('PEM'),
         }
+        data = self.encrypt(msgpack.packb(chain), password)
         with open(path, 'wb') as fd:
-            fd.write(msgpack.packb(chain))
+            fd.write(data)
             logging.info('Key chain "%s" saved', path)
 
     @staticmethod
-    def generate():
+    def generate(path, password):
         chain = KeyChain()
+        logging.info('Generating keys')
         chain.aes_id = os.urandom(32)
         chain.rsa_read = RSA.generate(2048)
         chain.rsa_create = RSA.generate(2048)
+        chain.save(path, password)
         return chain
 
 class CryptoManager(object):