switch to cryptography

Fernet could not be used as it is not a streaming cipher.
CFB mode does not require padding, so removed padding code.

Depends-On: https://review.openstack.org/c/575199/
Change-Id: I14f58d9d473c20ce7863baa0b2adadbfda268b7a
This commit is contained in:
Matthew Thode 2018-06-13 11:33:00 -05:00
parent 10814141a4
commit 7c16877195
No known key found for this signature in database
GPG Key ID: 64A37BEAAE19A4E8
5 changed files with 21 additions and 42 deletions

View File

@ -124,7 +124,7 @@ Linux Requirements
- libffi-dev
- libssl-dev
- python-dev
- pycrypto
- cryptography
- At least 128 MB of memory reserved for Freezer
Windows Requirements
@ -587,7 +587,7 @@ Freezer architectural components are the following:
- freezer client running on the node where the backups and restores are to be executed
Freezer uses GNU Tar or Rsync algorithm under the hood to execute incremental backup and
restore. When a key is provided, it uses OpenSSL or pycrypto module (OpenSSL compatible)
restore. When a key is provided, it uses OpenSSL or cryptography module (OpenSSL compatible)
to encrypt data. (AES-256-CFB)
=============

View File

@ -145,7 +145,6 @@ class Rsyncv2Engine(engine.BackupEngine):
flushed_data += compressor.flush()
if flushed_data and cipher:
flushed_data = cipher.encrypt(flushed_data)
flushed_data += cipher.flush()
return flushed_data

View File

@ -14,12 +14,15 @@
import hashlib
from Crypto.Cipher import AES
from Crypto import Random
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import algorithms
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers import modes
from os import urandom
SALT_HEADER = 'Salted__'
AES256_KEY_LENGTH = 32
BS = AES.block_size
BS = 16 # static 16 bytes, 128 bits for AES
class AESCipher(object):
@ -56,40 +59,21 @@ class AESEncrypt(AESCipher):
def __init__(self, pass_file):
super(AESEncrypt, self).__init__(pass_file)
self._salt = Random.new().read(BS - len(SALT_HEADER))
self._salt = urandom(BS - len(SALT_HEADER))
key, iv = self._derive_key_and_iv(self._password,
self._salt,
AES256_KEY_LENGTH,
BS)
self.cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=BS * 8)
self.remain = None
self.cipher = Cipher(algorithms.AES(key),
modes.CFB(iv),
backend=default_backend())
def generate_header(self):
return SALT_HEADER + self._salt
def encrypt(self, data):
remain = self.remain
if remain:
data = remain + data
remain = None
extra_bytes = len(data) % BS
if extra_bytes:
remain = data[-extra_bytes:]
data = data[:-extra_bytes]
self.remain = remain
return self.cipher.encrypt(data)
def flush(self):
def pad(s):
return s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
buff = self.remain
if buff:
return self.cipher.encrypt(pad(buff))
return b''
encryptor = self.cipher.encryptor()
return encryptor.update(data) + encryptor.finalize()
class AESDecrypt(AESCipher):
@ -105,17 +89,14 @@ class AESDecrypt(AESCipher):
self._salt,
AES256_KEY_LENGTH,
BS)
self.cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=BS * 8)
self.cipher = Cipher(algorithms.AES(key),
modes.CFB(iv),
backend=default_backend())
@staticmethod
def _prepare_salt(salt):
return salt[len(SALT_HEADER):]
def decrypt(self, data):
# def unpad(s):
# return s[0:-ord(s[-1])]
#
# if last_block:
# return unpad(self.cipher.decrypt(data))
# else:
return self.cipher.decrypt(data)
decryptor = self.cipher.decryptor()
return decryptor.update(data) + decryptor.finalize()

View File

@ -12,7 +12,7 @@ chardet==3.0.4
cliff==2.11.0
cmd2==0.8.1
coverage==4.0
cryptography==2.1.4
cryptography==2.1
ddt==1.0.1
debtcollector==1.19.0
decorator==4.2.1
@ -74,7 +74,6 @@ prettytable==0.7.2
psutil==3.2.2
pyasn1==0.4.2
pycparser==2.18
pycrypto==2.6
pyflakes==1.0.0
Pygments==2.2.0
pyinotify==0.9.6

View File

@ -18,7 +18,7 @@ keystoneauth1>=3.4.0 # Apache-2.0
os-brick>=2.2.0 # Apache-2.0
oslo.service!=1.28.1,>=1.24.0 # Apache-2.0
pycrypto>=2.6 # Public Domain
cryptography>=2.1 # Apache-2.0
PyMySQL>=0.7.6 # MIT License
pymongo!=3.1,>=3.0.2 # Apache-2.0
paramiko>=2.0.0 # LGPLv2.1+