diff -r 55e409ba4e72 -r 66fd59fecd68 components/openstack/heat/patches/02-nopycrypto.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/openstack/heat/patches/02-nopycrypto.patch Fri Feb 05 17:54:17 2016 -0500 @@ -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