--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/heat/patches/02-nopycrypto.patch Fri May 20 17:42:29 2016 -0400
@@ -0,0 +1,218 @@
+In-house removal of PyCrypto dependency in Heat. This patch is
+Solaris-specific and not suitable for upstream.
+
+--- heat-2015.1.2/heat/common/crypt.py.~1~ 2015-10-13 09:51:53.000000000 -0700
++++ heat-2015.1.2/heat/common/crypt.py 2016-01-28 00:39:30.968509417 -0800
+@@ -13,7 +13,6 @@
+
+ import base64
+
+-from Crypto.Cipher import AES
+ from oslo_config import cfg
+
+ from heat.openstack.common.crypto import utils
+@@ -59,9 +58,11 @@ def heat_decrypt(auth_info):
+ if auth_info is None:
+ return None
+ auth = base64.b64decode(auth_info)
+- iv = auth[:AES.block_size]
+- cipher = AES.new(cfg.CONF.auth_encryption_key[:32], 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-2015.1.2/heat/openstack/common/crypto/utils.py.~1~ 2015-10-13 09:51:50.000000000 -0700
++++ heat-2015.1.2/heat/openstack/common/crypto/utils.py 2016-01-28 00:39:30.935927064 -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