You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
3.3 KiB
Python
103 lines
3.3 KiB
Python
# (C) Copyright 2016 Mirantis, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import hashlib
|
|
|
|
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 = 16 # static 16 bytes, 128 bits for AES
|
|
|
|
|
|
class AESCipher(object):
|
|
"""
|
|
Base class for encrypt/decrypt activities.
|
|
"""
|
|
|
|
def __init__(self, pass_file):
|
|
self._password = self._get_pass_from_file(pass_file)
|
|
self._salt = None
|
|
self.cipher = None
|
|
|
|
@staticmethod
|
|
def _get_pass_from_file(pass_file):
|
|
with open(pass_file) as p_file:
|
|
password = p_file.readline()
|
|
return password
|
|
|
|
@staticmethod
|
|
def _derive_key_and_iv(password, salt, key_length, iv_length):
|
|
d = d_i = b''
|
|
while len(d) < key_length + iv_length:
|
|
md5_str = d_i + password + salt
|
|
d_i = hashlib.md5(md5_str).digest()
|
|
d += d_i
|
|
return d[:key_length], d[key_length:key_length + iv_length]
|
|
|
|
|
|
class AESEncrypt(AESCipher):
|
|
"""
|
|
Encrypts chucks of data using AES-256 algorithm.
|
|
OpenSSL compatible.
|
|
"""
|
|
|
|
def __init__(self, pass_file):
|
|
super(AESEncrypt, self).__init__(pass_file)
|
|
self._salt = urandom(BS - len(SALT_HEADER))
|
|
key, iv = self._derive_key_and_iv(self._password,
|
|
self._salt,
|
|
AES256_KEY_LENGTH,
|
|
BS)
|
|
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):
|
|
encryptor = self.cipher.encryptor()
|
|
return encryptor.update(data) + encryptor.finalize()
|
|
|
|
|
|
class AESDecrypt(AESCipher):
|
|
"""
|
|
Decrypts chucks of data using AES-256 algorithm.
|
|
OpenSSL compatible.
|
|
"""
|
|
|
|
def __init__(self, pass_file, salt):
|
|
super(AESDecrypt, self).__init__(pass_file)
|
|
self._salt = self._prepare_salt(salt)
|
|
key, iv = self._derive_key_and_iv(self._password,
|
|
self._salt,
|
|
AES256_KEY_LENGTH,
|
|
BS)
|
|
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):
|
|
decryptor = self.cipher.decryptor()
|
|
return decryptor.update(data) + decryptor.finalize()
|