|
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 |