heat/heat/common/crypt.py

97 lines
3.2 KiB
Python

#
# 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 base64
import sys
from Crypto.Cipher import AES
from cryptography import fernet
from oslo_config import cfg
from oslo_utils import encodeutils
from heat.common.i18n import _
from heat.openstack.common.crypto import utils
auth_opts = [
cfg.StrOpt('auth_encryption_key',
secret=True,
default='notgood but just long enough i t',
help=_('Key used to encrypt authentication info in the '
'database. Length of this key must be 32 characters.'))
]
cfg.CONF.register_opts(auth_opts)
def encrypt(value, encryption_key=None):
if value is None:
return None, None
encryption_key = get_valid_encryption_key(encryption_key, fix_length=True)
sym = fernet.Fernet(encryption_key.encode('base64'))
res = sym.encrypt(encodeutils.safe_encode(value))
return 'cryptography_decrypt_v1', res
def decrypt(method, data, encryption_key=None):
if method is None or data is None:
return None
decryptor = getattr(sys.modules[__name__], method)
value = decryptor(data, encryption_key)
if value is not None:
return encodeutils.safe_decode(value, 'utf-8')
def oslo_decrypt_v1(value, encryption_key=None):
encryption_key = get_valid_encryption_key(encryption_key)
sym = utils.SymmetricCrypto()
return sym.decrypt(encryption_key,
value, b64decode=True)
def cryptography_decrypt_v1(value, encryption_key=None):
encryption_key = get_valid_encryption_key(encryption_key, fix_length=True)
sym = fernet.Fernet(encryption_key.encode('base64'))
return sym.decrypt(encodeutils.safe_encode(value))
def get_valid_encryption_key(encryption_key, fix_length=False):
if encryption_key is None:
encryption_key = cfg.CONF.auth_encryption_key
if fix_length and len(encryption_key) < 32:
# Backward compatible size
encryption_key = encryption_key * 2
return encryption_key[:32]
def heat_decrypt(value, encryption_key=None):
"""Decrypt function for data that has been encrypted using an older
version of Heat.
Note: the encrypt function returns the function that is needed to
decrypt the data. The database then stores this. When the data is
then retrieved (potentially by a later version of Heat) the decrypt
function must still exist. So whilst it may seem that this function
is not referenced, it will be referenced from the database.
"""
encryption_key = get_valid_encryption_key(encryption_key)
auth = base64.b64decode(value)
iv = auth[:AES.block_size]
cipher = AES.new(encryption_key, AES.MODE_CFB, iv)
res = cipher.decrypt(auth[AES.block_size:])
return res
def list_opts():
yield None, auth_opts