24465652 Update Heat for the Mitaka release
21512755 HEAT service fails after deploying broken HOT template
In-house removal of PyCrypto dependency in Heat. This patch is
Solaris-specific and not suitable for upstream.
--- heat-5e1c8cb19eaee7570c2e1ca96c330b8d7d77a719/heat/common/crypt.py.~2~ 2016-02-02 01:40:32.301153073 -0800
+++ heat-5e1c8cb19eaee7570c2e1ca96c330b8d7d77a719/heat/common/crypt.py 2016-02-02 01:40:52.942307172 -0800
@@ -14,7 +14,6 @@
import base64
import sys
-from Crypto.Cipher import AES
from cryptography import fernet
from oslo_config import cfg
from oslo_utils import encodeutils
@@ -88,9 +87,11 @@ def heat_decrypt(value, encryption_key=N
"""
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:])
+ iv = auth[:16]
+ cipher = Cipher(alg='aes_256_cfb', key=cfg.CONF.auth_encryption_key[:32],
+ iv=iv, op=0)
+ padded = cipher.update(auth[16:])
+ res = padded + cipher.final()
return res
--- heat-5e1c8cb19eaee7570c2e1ca96c330b8d7d77a719/heat/openstack/common/crypto/utils.py.~2~ 2016-02-02 01:41:07.005491185 -0800
+++ heat-5e1c8cb19eaee7570c2e1ca96c330b8d7d77a719/heat/openstack/common/crypto/utils.py 2016-02-02 01:50:03.227200903 -0800
@@ -27,8 +27,8 @@
import base64
-from Crypto.Hash import HMAC
-from Crypto import Random
+from M2Crypto import EVP
+from M2Crypto import Rand
from oslo_utils import importutils
import six
@@ -36,6 +36,24 @@ from heat.openstack.common._i18n import
bchr = six.int2byte
+# Provide a mapping between the names of hash types used by PyCrypto to
+# their digest sizes and the corresponding algorithm name used by
+# M2Crypto/OpenSSL.
+hashmap = {
+ 'SHA224': (28, 'sha224'),
+ 'SHA256': (32, 'sha256'),
+ 'SHA384': (48, 'sha384'),
+ 'SHA512': (64, 'sha512')
+}
+
+# Provide a mapping between the length of a key and the algorithm name
+# used by M2Crypto/OpenSSL.
+algomap = {
+ 16: 'aes_128_cbc',
+ 24: 'aes_192_cbc',
+ 32: 'aes_256_cbc'
+}
+
class CryptoutilsException(Exception):
"""Generic Exception for Crypto utilities."""
@@ -52,6 +70,33 @@ class CipherBlockLengthTooBig(Cryptoutil
super(CryptoutilsException, self).__init__(message)
+class CipherKeyLengthInvalid(CryptoutilsException):
+ """The encryption key length is invalid for AES-CBC."""
+
+ def __init__(self, keylen):
+ msg = _("Encryption key length of %d is invalid for AES-CBC.")
+ message = msg % keylen
+ super(CryptoutilsException, self).__init__(message)
+
+
+class CipherTypeNotSupported(CryptoutilsException):
+ """The encryption cipher type is not supported."""
+
+ def __init__(self, enctype):
+ msg = _("Encryption cipher type %s is not supported")
+ message = msg % enctype
+ super(CryptoutilsException, self).__init__(message)
+
+
+class HashTypeNotSupported(CryptoutilsException):
+ """The message authentication hash function is not supported."""
+
+ def __init__(self, hashtype):
+ msg = _("Message authentication hash function %s is not supported")
+ message = msg % hashtype
+ super(CryptoutilsException, self).__init__(message)
+
+
class HKDFOutputLengthTooLong(CryptoutilsException):
"""The amount of Key Material asked is too much."""
@@ -68,8 +113,10 @@ class HKDF(object):
"""
def __init__(self, hashtype='SHA256'):
- self.hashfn = importutils.import_module('Crypto.Hash.' + hashtype)
- self.max_okm_length = 255 * self.hashfn.digest_size
+ if hashtype not in hashmap:
+ raise HashTypeNotSupported(hashtype)
+ (self.digest_size, self.algo) = hashmap[hashtype]
+ self.max_okm_length = 255 * self.digest_size
def extract(self, ikm, salt=None):
"""An extract function that can be used to derive a robust key given
@@ -80,9 +127,9 @@ class HKDF(object):
:param salt: optional salt value (a non-secret random value)
"""
if salt is None:
- salt = b'\x00' * self.hashfn.digest_size
+ salt = b'\x00' * self.digest_size
- return HMAC.new(salt, ikm, self.hashfn).digest()
+ return EVP.hmac(salt, ikm, self.algo)
def expand(self, prk, info, length):
"""An expand function that will return arbitrary length output that can
@@ -96,12 +143,12 @@ class HKDF(object):
if length > self.max_okm_length:
raise HKDFOutputLengthTooLong(length, self.max_okm_length)
- N = (length + self.hashfn.digest_size - 1) // self.hashfn.digest_size
+ N = (length + self.digest_size - 1) // self.digest_size
okm = b""
tmp = b""
for block in range(1, N + 1):
- tmp = HMAC.new(prk, tmp + info + bchr(block), self.hashfn).digest()
+ tmp = EVP.hmac(prk, tmp + info + bchr(block), self.algo)
okm += tmp
return okm[:length]
@@ -121,11 +168,15 @@ class SymmetricCrypto(object):
"""
def __init__(self, enctype='AES', hashtype='SHA256'):
- self.cipher = importutils.import_module('Crypto.Cipher.' + enctype)
- self.hashfn = importutils.import_module('Crypto.Hash.' + hashtype)
+ if enctype != 'AES':
+ raise CipherTypeNotSupported(enctype)
+ if hashtype not in hashmap:
+ raise HashTypeNotSupported(hashtype)
+ self.algo = hashmap[hashtype][1]
+ self.block_size = 16
def new_key(self, size):
- return Random.new().read(size)
+ return Rand.rand_bytes(size)
def encrypt(self, key, msg, b64encode=True):
"""Encrypt the provided msg and returns the cyphertext optionally
@@ -142,19 +193,14 @@ class SymmetricCrypto(object):
:returns enc: a block of encrypted data.
"""
- iv = Random.new().read(self.cipher.block_size)
- cipher = self.cipher.new(key, self.cipher.MODE_CBC, iv)
-
- # CBC mode requires a fixed block size. Append padding and length of
- # padding.
- if self.cipher.block_size > MAX_CB_SIZE:
- raise CipherBlockLengthTooBig(self.cipher.block_size, MAX_CB_SIZE)
- r = len(msg) % self.cipher.block_size
- padlen = self.cipher.block_size - r - 1
- msg += b'\x00' * padlen
- msg += bchr(padlen)
+ keylen = len(key)
+ if keylen not in algomap:
+ raise CipherKeyLengthInvalid(keylen)
+ iv = Rand.rand_bytes(self.block_size)
+ cipher = EVP.Cipher(algomap[keylen], key, iv, 1)
- enc = iv + cipher.encrypt(msg)
+ enc = iv + cipher.update(msg)
+ enc += cipher.final()
if b64encode:
enc = base64.b64encode(enc)
return enc
@@ -170,14 +216,16 @@ class SymmetricCrypto(object):
:returns plain: the plaintext message.
"""
+ keylen = len(key)
+ if keylen not in algomap:
+ raise CipherKeyLengthInvalid(keylen)
if b64decode:
msg = base64.b64decode(msg)
- iv = msg[:self.cipher.block_size]
- cipher = self.cipher.new(key, self.cipher.MODE_CBC, iv)
+ iv = msg[:self.block_size]
+ cipher = EVP.Cipher(algomap[keylen], key, iv, 0)
- padded = cipher.decrypt(msg[self.cipher.block_size:])
- l = ord(padded[-1:]) + 1
- plain = padded[:-l]
+ padded = cipher.update(msg[self.block_size:])
+ plain = padded + cipher.final()
return plain
def sign(self, key, msg, b64encode=True):
@@ -190,8 +238,7 @@ class SymmetricCrypto(object):
:returns out: a base64 encoded signature.
"""
- h = HMAC.new(key, msg, self.hashfn)
- out = h.digest()
+ out = EVP.hmac(key, msg, self.algo)
if b64encode:
out = base64.b64encode(out)
return out