components/openstack/heat/patches/02-nopycrypto.patch
changeset 5405 66fd59fecd68
parent 3998 5bd484384122
child 6850 f8d3bc724af7
equal deleted inserted replaced
5404:55e409ba4e72 5405:66fd59fecd68
       
     1 In-house removal of PyCrypto dependency in Heat. This patch is
       
     2 Solaris-specific and not suitable for upstream.
       
     3 
       
     4 --- heat-2015.1.2/heat/common/crypt.py.~1~	2015-10-13 09:51:53.000000000 -0700
       
     5 +++ heat-2015.1.2/heat/common/crypt.py	2016-01-28 00:39:30.968509417 -0800
       
     6 @@ -13,7 +13,6 @@
       
     7  
       
     8  import base64
       
     9  
       
    10 -from Crypto.Cipher import AES
       
    11  from oslo_config import cfg
       
    12  
       
    13  from heat.openstack.common.crypto import utils
       
    14 @@ -59,9 +58,11 @@ def heat_decrypt(auth_info):
       
    15      if auth_info is None:
       
    16          return None
       
    17      auth = base64.b64decode(auth_info)
       
    18 -    iv = auth[:AES.block_size]
       
    19 -    cipher = AES.new(cfg.CONF.auth_encryption_key[:32], AES.MODE_CFB, iv)
       
    20 -    res = cipher.decrypt(auth[AES.block_size:])
       
    21 +    iv = auth[:16]
       
    22 +    cipher = Cipher(alg='aes_256_cfb', key=cfg.CONF.auth_encryption_key[:32],
       
    23 +                    iv=iv, op=0)
       
    24 +    padded = cipher.update(auth[16:])
       
    25 +    res = padded + cipher.final()
       
    26      return res
       
    27  
       
    28  
       
    29 --- heat-2015.1.2/heat/openstack/common/crypto/utils.py.~1~	2015-10-13 09:51:50.000000000 -0700
       
    30 +++ heat-2015.1.2/heat/openstack/common/crypto/utils.py	2016-01-28 00:39:30.935927064 -0800
       
    31 @@ -27,8 +27,8 @@
       
    32  
       
    33  import base64
       
    34  
       
    35 -from Crypto.Hash import HMAC
       
    36 -from Crypto import Random
       
    37 +from M2Crypto import EVP
       
    38 +from M2Crypto import Rand
       
    39  from oslo_utils import importutils
       
    40  import six
       
    41  
       
    42 @@ -36,6 +36,24 @@ from heat.openstack.common._i18n import
       
    43  
       
    44  bchr = six.int2byte
       
    45  
       
    46 +# Provide a mapping between the names of hash types used by PyCrypto to
       
    47 +# their digest sizes and the corresponding algorithm name used by
       
    48 +# M2Crypto/OpenSSL.
       
    49 +hashmap = {
       
    50 +    'SHA224':   (28, 'sha224'),
       
    51 +    'SHA256':   (32, 'sha256'),
       
    52 +    'SHA384':   (48, 'sha384'),
       
    53 +    'SHA512':   (64, 'sha512')
       
    54 +}
       
    55 +
       
    56 +# Provide a mapping between the length of a key and the algorithm name
       
    57 +# used by M2Crypto/OpenSSL.
       
    58 +algomap = {
       
    59 +    16:         'aes_128_cbc',
       
    60 +    24:         'aes_192_cbc',
       
    61 +    32:         'aes_256_cbc'
       
    62 +}
       
    63 +
       
    64  
       
    65  class CryptoutilsException(Exception):
       
    66      """Generic Exception for Crypto utilities."""
       
    67 @@ -52,6 +70,33 @@ class CipherBlockLengthTooBig(Cryptoutil
       
    68          super(CryptoutilsException, self).__init__(message)
       
    69  
       
    70  
       
    71 +class CipherKeyLengthInvalid(CryptoutilsException):
       
    72 +    """The encryption key length is invalid for AES-CBC."""
       
    73 +
       
    74 +    def __init__(self, keylen):
       
    75 +        msg = _("Encryption key length of %d is invalid for AES-CBC.")
       
    76 +        message = msg % keylen
       
    77 +        super(CryptoutilsException, self).__init__(message)
       
    78 +
       
    79 +
       
    80 +class CipherTypeNotSupported(CryptoutilsException):
       
    81 +    """The encryption cipher type is not supported."""
       
    82 +
       
    83 +    def __init__(self, enctype):
       
    84 +        msg = _("Encryption cipher type %s is not supported")
       
    85 +        message = msg % enctype
       
    86 +        super(CryptoutilsException, self).__init__(message)
       
    87 +
       
    88 +
       
    89 +class HashTypeNotSupported(CryptoutilsException):
       
    90 +    """The message authentication hash function is not supported."""
       
    91 +
       
    92 +    def __init__(self, hashtype):
       
    93 +        msg = _("Message authentication hash function %s is not supported")
       
    94 +        message = msg % hashtype
       
    95 +        super(CryptoutilsException, self).__init__(message)
       
    96 +
       
    97 +
       
    98  class HKDFOutputLengthTooLong(CryptoutilsException):
       
    99      """The amount of Key Material asked is too much."""
       
   100  
       
   101 @@ -68,8 +113,10 @@ class HKDF(object):
       
   102      """
       
   103  
       
   104      def __init__(self, hashtype='SHA256'):
       
   105 -        self.hashfn = importutils.import_module('Crypto.Hash.' + hashtype)
       
   106 -        self.max_okm_length = 255 * self.hashfn.digest_size
       
   107 +        if hashtype not in hashmap:
       
   108 +            raise HashTypeNotSupported(hashtype)
       
   109 +        (self.digest_size, self.algo) = hashmap[hashtype]
       
   110 +        self.max_okm_length = 255 * self.digest_size
       
   111  
       
   112      def extract(self, ikm, salt=None):
       
   113          """An extract function that can be used to derive a robust key given
       
   114 @@ -80,9 +127,9 @@ class HKDF(object):
       
   115          :param salt: optional salt value (a non-secret random value)
       
   116          """
       
   117          if salt is None:
       
   118 -            salt = b'\x00' * self.hashfn.digest_size
       
   119 +            salt = b'\x00' * self.digest_size
       
   120  
       
   121 -        return HMAC.new(salt, ikm, self.hashfn).digest()
       
   122 +        return EVP.hmac(salt, ikm, self.algo)
       
   123  
       
   124      def expand(self, prk, info, length):
       
   125          """An expand function that will return arbitrary length output that can
       
   126 @@ -96,12 +143,12 @@ class HKDF(object):
       
   127          if length > self.max_okm_length:
       
   128              raise HKDFOutputLengthTooLong(length, self.max_okm_length)
       
   129  
       
   130 -        N = (length + self.hashfn.digest_size - 1) // self.hashfn.digest_size
       
   131 +        N = (length + self.digest_size - 1) // self.digest_size
       
   132  
       
   133          okm = b""
       
   134          tmp = b""
       
   135          for block in range(1, N + 1):
       
   136 -            tmp = HMAC.new(prk, tmp + info + bchr(block), self.hashfn).digest()
       
   137 +            tmp = EVP.hmac(prk, tmp + info + bchr(block), self.algo)
       
   138              okm += tmp
       
   139  
       
   140          return okm[:length]
       
   141 @@ -121,11 +168,15 @@ class SymmetricCrypto(object):
       
   142      """
       
   143  
       
   144      def __init__(self, enctype='AES', hashtype='SHA256'):
       
   145 -        self.cipher = importutils.import_module('Crypto.Cipher.' + enctype)
       
   146 -        self.hashfn = importutils.import_module('Crypto.Hash.' + hashtype)
       
   147 +        if enctype != 'AES':
       
   148 +            raise CipherTypeNotSupported(enctype)
       
   149 +        if hashtype not in hashmap:
       
   150 +            raise HashTypeNotSupported(hashtype)
       
   151 +        self.algo = hashmap[hashtype][1]
       
   152 +        self.block_size = 16
       
   153  
       
   154      def new_key(self, size):
       
   155 -        return Random.new().read(size)
       
   156 +        return Rand.rand_bytes(size)
       
   157  
       
   158      def encrypt(self, key, msg, b64encode=True):
       
   159          """Encrypt the provided msg and returns the cyphertext optionally
       
   160 @@ -142,19 +193,14 @@ class SymmetricCrypto(object):
       
   161  
       
   162          :returns enc: a block of encrypted data.
       
   163          """
       
   164 -        iv = Random.new().read(self.cipher.block_size)
       
   165 -        cipher = self.cipher.new(key, self.cipher.MODE_CBC, iv)
       
   166 -
       
   167 -        # CBC mode requires a fixed block size. Append padding and length of
       
   168 -        # padding.
       
   169 -        if self.cipher.block_size > MAX_CB_SIZE:
       
   170 -            raise CipherBlockLengthTooBig(self.cipher.block_size, MAX_CB_SIZE)
       
   171 -        r = len(msg) % self.cipher.block_size
       
   172 -        padlen = self.cipher.block_size - r - 1
       
   173 -        msg += b'\x00' * padlen
       
   174 -        msg += bchr(padlen)
       
   175 +        keylen = len(key)
       
   176 +        if keylen not in algomap:
       
   177 +            raise CipherKeyLengthInvalid(keylen)
       
   178 +        iv = Rand.rand_bytes(self.block_size)
       
   179 +        cipher = EVP.Cipher(algomap[keylen], key, iv, 1)
       
   180  
       
   181 -        enc = iv + cipher.encrypt(msg)
       
   182 +        enc = iv + cipher.update(msg)
       
   183 +        enc += cipher.final()
       
   184          if b64encode:
       
   185              enc = base64.b64encode(enc)
       
   186          return enc
       
   187 @@ -170,14 +216,16 @@ class SymmetricCrypto(object):
       
   188  
       
   189          :returns plain: the plaintext message.
       
   190          """
       
   191 +        keylen = len(key)
       
   192 +        if keylen not in algomap:
       
   193 +            raise CipherKeyLengthInvalid(keylen)
       
   194          if b64decode:
       
   195              msg = base64.b64decode(msg)
       
   196 -        iv = msg[:self.cipher.block_size]
       
   197 -        cipher = self.cipher.new(key, self.cipher.MODE_CBC, iv)
       
   198 +        iv = msg[:self.block_size]
       
   199 +        cipher = EVP.Cipher(algomap[keylen], key, iv, 0)
       
   200  
       
   201 -        padded = cipher.decrypt(msg[self.cipher.block_size:])
       
   202 -        l = ord(padded[-1:]) + 1
       
   203 -        plain = padded[:-l]
       
   204 +        padded = cipher.update(msg[self.block_size:])
       
   205 +        plain = padded + cipher.final()
       
   206          return plain
       
   207  
       
   208      def sign(self, key, msg, b64encode=True):
       
   209 @@ -190,8 +238,7 @@ class SymmetricCrypto(object):
       
   210  
       
   211          :returns out: a base64 encoded signature.
       
   212          """
       
   213 -        h = HMAC.new(key, msg, self.hashfn)
       
   214 -        out = h.digest()
       
   215 +        out = EVP.hmac(key, msg, self.algo)
       
   216          if b64encode:
       
   217              out = base64.b64encode(out)
       
   218          return out