Gentoo Archives: gentoo-commits

From: Matt Thode <prometheanfire@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] repo/gentoo:master commit in: app-admin/ansible/files/, app-admin/ansible/
Date: Thu, 01 Jun 2017 23:55:49
Message-Id: 1496361331.eee983b6a6b8cf09256d37f46ab28117f317a3ee.prometheanfire@gentoo
1 commit: eee983b6a6b8cf09256d37f46ab28117f317a3ee
2 Author: Matthew Thode <prometheanfire <AT> gentoo <DOT> org>
3 AuthorDate: Thu Jun 1 23:55:00 2017 +0000
4 Commit: Matt Thode <prometheanfire <AT> gentoo <DOT> org>
5 CommitDate: Thu Jun 1 23:55:31 2017 +0000
6 URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=eee983b6
7
8 app-admin/ansible: 2.3.1.0 bup
9
10 Package-Manager: Portage-2.3.5, Repoman-2.3.2
11
12 app-admin/ansible/Manifest | 1 +
13 app-admin/ansible/ansible-2.3.1.0.ebuild | 60 ++
14 .../files/ansible-2.3.1.0-pycryptodome.patch | 905 +++++++++++++++++++++
15 3 files changed, 966 insertions(+)
16
17 diff --git a/app-admin/ansible/Manifest b/app-admin/ansible/Manifest
18 index f65d26d2619..fb28f16016d 100644
19 --- a/app-admin/ansible/Manifest
20 +++ b/app-admin/ansible/Manifest
21 @@ -1,2 +1,3 @@
22 DIST ansible-2.3.0.0-pycryptodome.patch 32449 SHA256 a87d712305160ef8a559bf9e27d7fe4361889f45da5ba501c4a9ae8c6c58e504 SHA512 f52611275262d772b2e44b858c59590ab18ed29c22899c0d8b47f3a21c71aebff9aab1a2284bdf44fbc47b63c3836851db1c065bafe0b6883bb582a98cf4d0a4 WHIRLPOOL bd344c2507eb8576a114e5618d182b90443a92d4cd57f27310eb495dbba123853503c64c69338a02ce680b615862c819985913f2860e3dde670ef929dcd5761f
23 DIST ansible-2.3.0.0.tar.gz 4251730 SHA256 299f3907cd566a20e163942fa82b6afc86ef89c2726ba503b90c1a651e82a458 SHA512 88ac28befefd7a70c36d8c33bc1aba1b0a5ffdea4bddd0b9e6c5488c70057662812208c221e47721c5a194fc30282a33490f196a719d9eb6d9b1e7dcfd1ff941 WHIRLPOOL a6a622f17476c07e3446a7b09631027797ade04f7d1571e0eafccc2736390deabfcdd36fa1e849d209eb7ab5f1e3e86f2b6e7dd3032db1743665165c1dc710ba
24 +DIST ansible-2.3.1.0.tar.gz 4263357 SHA256 cd4b8f53720fcd0c351156b840fdd15ecfbec22c951b5406ec503de49d40b9f5 SHA512 7b4b33c56a15c41d756f095944d7a0dbf894557350879430df21061b717b9574aae624a276bf7e1a13d043b718aeaccac1ce510a3cb085983311ddf06fa832bc WHIRLPOOL 5552ff64e8cac722a705bd1e72465c8bfe583a1c5efc587ca141b34eaa35d2af0ea2e1084a072ecb7dbb28d59d7f56e47c4c73f9e3e0f7f2451ef4e7134ad1af
25
26 diff --git a/app-admin/ansible/ansible-2.3.1.0.ebuild b/app-admin/ansible/ansible-2.3.1.0.ebuild
27 new file mode 100644
28 index 00000000000..1e73cf04d35
29 --- /dev/null
30 +++ b/app-admin/ansible/ansible-2.3.1.0.ebuild
31 @@ -0,0 +1,60 @@
32 +# Copyright 1999-2017 Gentoo Foundation
33 +# Distributed under the terms of the GNU General Public License v2
34 +
35 +EAPI=6
36 +
37 +PYTHON_COMPAT=( python2_7 )
38 +
39 +inherit distutils-r1 eutils versionator
40 +
41 +DESCRIPTION="Model-driven deployment, config management, and command execution framework"
42 +HOMEPAGE="http://ansible.com/"
43 +SRC_URI="http://releases.ansible.com/${PN}/${P}.tar.gz"
44 +
45 +LICENSE="GPL-3"
46 +SLOT="0"
47 +KEYWORDS="~amd64 ~x86 ~x64-macos"
48 +IUSE="test"
49 +
50 +RDEPEND="
51 + dev-python/paramiko[${PYTHON_USEDEP}]
52 + dev-python/jinja[${PYTHON_USEDEP}]
53 + dev-python/pyyaml[${PYTHON_USEDEP}]
54 + dev-python/setuptools[${PYTHON_USEDEP}]
55 + || (
56 + dev-python/pycryptodome[${PYTHON_USEDEP}]
57 + >=dev-python/pycrypto-2.6[${PYTHON_USEDEP}]
58 + )
59 + dev-python/httplib2[${PYTHON_USEDEP}]
60 + dev-python/six[${PYTHON_USEDEP}]
61 + net-misc/sshpass
62 + virtual/ssh
63 +"
64 +DEPEND="
65 + dev-python/setuptools[${PYTHON_USEDEP}]
66 + >=dev-python/packaging-16.6[${PYTHON_USEDEP}]
67 + test? (
68 + ${RDEPEND}
69 + dev-python/nose[${PYTHON_USEDEP}]
70 + >=dev-python/mock-1.0.1[${PYTHON_USEDEP}]
71 + <dev-python/mock-1.1[${PYTHON_USEDEP}]
72 + dev-python/passlib[${PYTHON_USEDEP}]
73 + dev-python/coverage[${PYTHON_USEDEP}]
74 + dev-python/unittest2[${PYTHON_USEDEP}]
75 + dev-vcs/git
76 + )"
77 +
78 +# not included in release tarball
79 +RESTRICT="test"
80 +
81 +PATCHES=( "${FILESDIR}/${P}-pycryptodome.patch" )
82 +
83 +python_test() {
84 + nosetests -d -w test/units -v --with-coverage --cover-package=ansible --cover-branches || die
85 +}
86 +
87 +python_install_all() {
88 + distutils-r1_python_install_all
89 +
90 + doman docs/man/man1/*.1
91 +}
92
93 diff --git a/app-admin/ansible/files/ansible-2.3.1.0-pycryptodome.patch b/app-admin/ansible/files/ansible-2.3.1.0-pycryptodome.patch
94 new file mode 100644
95 index 00000000000..71799dec1ab
96 --- /dev/null
97 +++ b/app-admin/ansible/files/ansible-2.3.1.0-pycryptodome.patch
98 @@ -0,0 +1,905 @@
99 +diff -uNr ansible-2.3.0.0.ORIG/lib/ansible/executor/process/worker.py ansible-2.3.0.0/lib/ansible/executor/process/worker.py
100 +--- ansible-2.3.0.0.ORIG/lib/ansible/executor/process/worker.py 2017-05-23 14:23:12.313595450 +0100
101 ++++ ansible-2.3.0.0/lib/ansible/executor/process/worker.py 2017-05-23 14:24:22.116598926 +0100
102 +@@ -26,14 +26,6 @@
103 +
104 + from jinja2.exceptions import TemplateNotFound
105 +
106 +-# TODO: not needed if we use the cryptography library with its default RNG
107 +-# engine
108 +-HAS_ATFORK=True
109 +-try:
110 +- from Crypto.Random import atfork
111 +-except ImportError:
112 +- HAS_ATFORK=False
113 +-
114 + from ansible.errors import AnsibleConnectionFailure
115 + from ansible.executor.task_executor import TaskExecutor
116 + from ansible.executor.task_result import TaskResult
117 +@@ -95,9 +87,6 @@
118 + #pr = cProfile.Profile()
119 + #pr.enable()
120 +
121 +- if HAS_ATFORK:
122 +- atfork()
123 +-
124 + try:
125 + # execute the task and build a TaskResult from the result
126 + display.debug("running TaskExecutor() for %s/%s" % (self._host, self._task))
127 +diff -uNr ansible-2.3.0.0.ORIG/lib/ansible/modules/cloud/amazon/ec2_win_password.py ansible-2.3.0.0/lib/ansible/modules/cloud/amazon/ec2_win_password.py
128 +--- ansible-2.3.0.0.ORIG/lib/ansible/modules/cloud/amazon/ec2_win_password.py 2017-05-23 14:23:12.299595449 +0100
129 ++++ ansible-2.3.0.0/lib/ansible/modules/cloud/amazon/ec2_win_password.py 2017-05-23 14:29:51.003615305 +0100
130 +@@ -93,9 +93,10 @@
131 + '''
132 +
133 + from base64 import b64decode
134 +-from Crypto.Cipher import PKCS1_v1_5
135 +-from Crypto.PublicKey import RSA
136 + import datetime
137 ++from cryptography.hazmat.backends import default_backend
138 ++from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
139 ++from cryptography.hazmat.primitives.serialization import load_pem_private_key
140 +
141 + try:
142 + import boto.ec2
143 +@@ -103,6 +104,9 @@
144 + except ImportError:
145 + HAS_BOTO = False
146 +
147 ++BACKEND = default_backend()
148 ++
149 ++
150 + def main():
151 + argument_spec = ec2_argument_spec()
152 + argument_spec.update(dict(
153 +@@ -151,15 +155,12 @@
154 + else:
155 + try:
156 + with f:
157 +- key = RSA.importKey(f.read(), key_passphrase)
158 +- except (ValueError, IndexError, TypeError) as e:
159 ++ key = load_pem_private_key(f.read(), key_passphrase, BACKEND)
160 ++ except (ValueError, TypeError) as e:
161 + module.fail_json(msg = "unable to parse key file")
162 +
163 +- cipher = PKCS1_v1_5.new(key)
164 +- sentinel = 'password decryption failed!!!'
165 +-
166 + try:
167 +- decrypted = cipher.decrypt(decoded, sentinel)
168 ++ decrypted = key.decrypt(decoded, PKCS1v15())
169 + except ValueError as e:
170 + decrypted = None
171 +
172 +diff -uNr ansible-2.3.0.0.ORIG/lib/ansible/parsing/vault/__init__.py ansible-2.3.0.0/lib/ansible/parsing/vault/__init__.py
173 +--- ansible-2.3.0.0.ORIG/lib/ansible/parsing/vault/__init__.py 2017-05-23 14:23:12.311595449 +0100
174 ++++ ansible-2.3.0.0/lib/ansible/parsing/vault/__init__.py 2017-05-23 14:31:23.267619901 +0100
175 +@@ -25,7 +25,6 @@
176 + import sys
177 + import tempfile
178 + import random
179 +-from io import BytesIO
180 + from subprocess import call
181 + from hashlib import sha256
182 + from binascii import hexlify
183 +@@ -35,32 +34,14 @@
184 + # Note: Only used for loading obsolete VaultAES files. All files are written
185 + # using the newer VaultAES256 which does not require md5
186 +
187 +-try:
188 +- from Crypto.Hash import SHA256, HMAC
189 +- HAS_HASH = True
190 +-except ImportError:
191 +- HAS_HASH = False
192 +-
193 +-# Counter import fails for 2.0.1, requires >= 2.6.1 from pip
194 +-try:
195 +- from Crypto.Util import Counter
196 +- HAS_COUNTER = True
197 +-except ImportError:
198 +- HAS_COUNTER = False
199 +-
200 +-# KDF import fails for 2.0.1, requires >= 2.6.1 from pip
201 +-try:
202 +- from Crypto.Protocol.KDF import PBKDF2
203 +- HAS_PBKDF2 = True
204 +-except ImportError:
205 +- HAS_PBKDF2 = False
206 +-
207 +-# AES IMPORTS
208 +-try:
209 +- from Crypto.Cipher import AES as AES
210 +- HAS_AES = True
211 +-except ImportError:
212 +- HAS_AES = False
213 ++from cryptography.exceptions import InvalidSignature
214 ++from cryptography.hazmat.backends import default_backend
215 ++from cryptography.hazmat.primitives import hashes, padding
216 ++from cryptography.hazmat.primitives.hmac import HMAC
217 ++from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
218 ++from cryptography.hazmat.primitives.ciphers import (
219 ++ Cipher as C_Cipher, algorithms, modes
220 ++)
221 +
222 + from ansible.compat.six import PY3, binary_type
223 + from ansible.compat.six.moves import zip
224 +@@ -73,26 +54,8 @@
225 + from ansible.utils.display import Display
226 + display = Display()
227 +
228 +-# OpenSSL pbkdf2_hmac
229 +-HAS_PBKDF2HMAC = False
230 +-try:
231 +- from cryptography.hazmat.primitives.hashes import SHA256 as c_SHA256
232 +- from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
233 +- from cryptography.hazmat.backends import default_backend
234 +- HAS_PBKDF2HMAC = True
235 +-except ImportError:
236 +- pass
237 +-except Exception as e:
238 +- display.vvvv("Optional dependency 'cryptography' raised an exception, falling back to 'Crypto'.")
239 +- import traceback
240 +- display.vvvv("Traceback from import of cryptography was {0}".format(traceback.format_exc()))
241 +-
242 +-HAS_ANY_PBKDF2HMAC = HAS_PBKDF2 or HAS_PBKDF2HMAC
243 +-
244 +-
245 +-CRYPTO_UPGRADE = "ansible-vault requires a newer version of pycrypto than the one installed on your platform." \
246 +- " You may fix this with OS-specific commands such as: yum install python-devel; rpm -e --nodeps python-crypto; pip install pycrypto"
247 +
248 ++BACKEND = default_backend()
249 + b_HEADER = b'$ANSIBLE_VAULT'
250 + CIPHER_WHITELIST = frozenset((u'AES', u'AES256'))
251 + CIPHER_WRITE_WHITELIST = frozenset((u'AES256',))
252 +@@ -100,12 +63,6 @@
253 + # (used in VaultFile header) to a cipher class
254 +
255 +
256 +-def check_prereqs():
257 +-
258 +- if not HAS_AES or not HAS_COUNTER or not HAS_ANY_PBKDF2HMAC or not HAS_HASH:
259 +- raise AnsibleError(CRYPTO_UPGRADE)
260 +-
261 +-
262 + class AnsibleVaultError(AnsibleError):
263 + pass
264 +
265 +@@ -410,8 +367,6 @@
266 +
267 + def encrypt_file(self, filename, output_file=None):
268 +
269 +- check_prereqs()
270 +-
271 + # A file to be encrypted into a vaultfile could be any encoding
272 + # so treat the contents as a byte string.
273 +
274 +@@ -424,8 +379,6 @@
275 +
276 + def decrypt_file(self, filename, output_file=None):
277 +
278 +- check_prereqs()
279 +-
280 + # follow the symlink
281 + filename = os.path.realpath(filename)
282 +
283 +@@ -440,8 +393,6 @@
284 + def create_file(self, filename):
285 + """ create a new encrypted file """
286 +
287 +- check_prereqs()
288 +-
289 + # FIXME: If we can raise an error here, we can probably just make it
290 + # behave like edit instead.
291 + if os.path.isfile(filename):
292 +@@ -451,8 +402,6 @@
293 +
294 + def edit_file(self, filename):
295 +
296 +- check_prereqs()
297 +-
298 + # follow the symlink
299 + filename = os.path.realpath(filename)
300 +
301 +@@ -471,7 +420,6 @@
302 +
303 + def plaintext(self, filename):
304 +
305 +- check_prereqs()
306 + ciphertext = self.read_data(filename)
307 +
308 + try:
309 +@@ -483,8 +431,6 @@
310 +
311 + def rekey_file(self, filename, b_new_password):
312 +
313 +- check_prereqs()
314 +-
315 + # follow the symlink
316 + filename = os.path.realpath(filename)
317 +
318 +@@ -581,10 +527,6 @@
319 +
320 + # Note: strings in this class should be byte strings by default.
321 +
322 +- def __init__(self):
323 +- if not HAS_AES:
324 +- raise AnsibleError(CRYPTO_UPGRADE)
325 +-
326 + def _aes_derive_key_and_iv(self, b_password, b_salt, key_length, iv_length):
327 +
328 + """ Create a key and an initialization vector """
329 +@@ -620,41 +562,22 @@
330 + ' switch to the newer VaultAES256 format', version='2.3')
331 + # http://stackoverflow.com/a/14989032
332 +
333 +- b_ciphertext = unhexlify(b_vaulttext)
334 +-
335 +- in_file = BytesIO(b_ciphertext)
336 +- in_file.seek(0)
337 +- out_file = BytesIO()
338 ++ b_vaultdata = unhexlify(b_vaulttext)
339 ++ b_tmpsalt = b_vaultdata[:16]
340 ++ b_ciphertext = b_vaultdata[16:]
341 +
342 +- bs = AES.block_size
343 +- b_tmpsalt = in_file.read(bs)
344 ++ bs = algorithms.AES.block_size // 8
345 + b_salt = b_tmpsalt[len(b'Salted__'):]
346 + b_key, b_iv = self._aes_derive_key_and_iv(b_password, b_salt, key_length, bs)
347 +- cipher = AES.new(b_key, AES.MODE_CBC, b_iv)
348 +- b_next_chunk = b''
349 +- finished = False
350 +-
351 +- while not finished:
352 +- b_chunk, b_next_chunk = b_next_chunk, cipher.decrypt(in_file.read(1024 * bs))
353 +- if len(b_next_chunk) == 0:
354 +- if PY3:
355 +- padding_length = b_chunk[-1]
356 +- else:
357 +- padding_length = ord(b_chunk[-1])
358 ++ cipher = C_Cipher(algorithms.AES(b_key), modes.CBC(b_iv), BACKEND).decryptor()
359 ++ unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
360 +
361 +- b_chunk = b_chunk[:-padding_length]
362 +- finished = True
363 +-
364 +- out_file.write(b_chunk)
365 +- out_file.flush()
366 +-
367 +- # reset the stream pointer to the beginning
368 +- out_file.seek(0)
369 +- b_out_data = out_file.read()
370 +- out_file.close()
371 ++ b_plaintext = unpadder.update(
372 ++ cipher.update(b_ciphertext) + cipher.finalize()
373 ++ ) + unpadder.finalize()
374 +
375 + # split out sha and verify decryption
376 +- b_split_data = b_out_data.split(b"\n", 1)
377 ++ b_split_data = b_plaintext.split(b"\n", 1)
378 + b_this_sha = b_split_data[0]
379 + b_plaintext = b_split_data[1]
380 + b_test_sha = to_bytes(sha256(b_plaintext).hexdigest())
381 +@@ -676,19 +599,16 @@
382 +
383 + # Note: strings in this class should be byte strings by default.
384 +
385 +- def __init__(self):
386 +-
387 +- check_prereqs()
388 +-
389 + @staticmethod
390 + def _create_key(b_password, b_salt, keylength, ivlength):
391 +- hash_function = SHA256
392 +-
393 +- # make two keys and one iv
394 +- pbkdf2_prf = lambda p, s: HMAC.new(p, s, hash_function).digest()
395 ++ kdf = PBKDF2HMAC(
396 ++ algorithm=hashes.SHA256(),
397 ++ length=2 * keylength + ivlength,
398 ++ salt=b_salt,
399 ++ iterations=10000,
400 ++ backend=BACKEND)
401 ++ b_derivedkey = kdf.derive(b_password)
402 +
403 +- b_derivedkey = PBKDF2(b_password, b_salt, dkLen=(2 * keylength) + ivlength,
404 +- count=10000, prf=pbkdf2_prf)
405 + return b_derivedkey
406 +
407 + @classmethod
408 +@@ -696,55 +616,31 @@
409 + # 16 for AES 128, 32 for AES256
410 + keylength = 32
411 +
412 +- # match the size used for counter.new to avoid extra work
413 +- ivlength = 16
414 ++ # AES is a 128-bit block cipher, so IVs and counter nonces are 16 bytes
415 ++ ivlength = algorithms.AES.block_size // 8
416 +
417 +- if HAS_PBKDF2HMAC:
418 +- backend = default_backend()
419 +- kdf = PBKDF2HMAC(
420 +- algorithm=c_SHA256(),
421 +- length=2 * keylength + ivlength,
422 +- salt=b_salt,
423 +- iterations=10000,
424 +- backend=backend)
425 +- b_derivedkey = kdf.derive(b_password)
426 +- else:
427 +- b_derivedkey = cls._create_key(b_password, b_salt, keylength, ivlength)
428 ++ b_derivedkey = cls._create_key(b_password, b_salt, keylength, ivlength)
429 +
430 + b_key1 = b_derivedkey[:keylength]
431 + b_key2 = b_derivedkey[keylength:(keylength * 2)]
432 + b_iv = b_derivedkey[(keylength * 2):(keylength * 2) + ivlength]
433 +
434 +- return b_key1, b_key2, hexlify(b_iv)
435 ++ return b_key1, b_key2, b_iv
436 +
437 + def encrypt(self, b_plaintext, b_password):
438 + b_salt = os.urandom(32)
439 + b_key1, b_key2, b_iv = self._gen_key_initctr(b_password, b_salt)
440 +
441 +- # PKCS#7 PAD DATA http://tools.ietf.org/html/rfc5652#section-6.3
442 +- bs = AES.block_size
443 +- padding_length = (bs - len(b_plaintext) % bs) or bs
444 +- b_plaintext += to_bytes(padding_length * chr(padding_length), encoding='ascii', errors='strict')
445 +-
446 +- # COUNTER.new PARAMETERS
447 +- # 1) nbits (integer) - Length of the counter, in bits.
448 +- # 2) initial_value (integer) - initial value of the counter. "iv" from _gen_key_initctr
449 +-
450 +- ctr = Counter.new(128, initial_value=int(b_iv, 16))
451 +-
452 +- # AES.new PARAMETERS
453 +- # 1) AES key, must be either 16, 24, or 32 bytes long -- "key" from _gen_key_initctr
454 +- # 2) MODE_CTR, is the recommended mode
455 +- # 3) counter=<CounterObject>
456 +-
457 +- cipher = AES.new(b_key1, AES.MODE_CTR, counter=ctr)
458 +-
459 +- # ENCRYPT PADDED DATA
460 +- b_ciphertext = cipher.encrypt(b_plaintext)
461 ++ cipher = C_Cipher(algorithms.AES(b_key1), modes.CTR(b_iv), BACKEND)
462 ++ encryptor = cipher.encryptor()
463 ++ padder = padding.PKCS7(algorithms.AES.block_size).padder()
464 ++ b_ciphertext = encryptor.update(padder.update(b_plaintext) + padder.finalize())
465 ++ b_ciphertext += encryptor.finalize()
466 +
467 + # COMBINE SALT, DIGEST AND DATA
468 +- hmac = HMAC.new(b_key2, b_ciphertext, SHA256)
469 +- b_vaulttext = b'\n'.join([hexlify(b_salt), to_bytes(hmac.hexdigest()), hexlify(b_ciphertext)])
470 ++ hmac = HMAC(b_key2, hashes.SHA256(), BACKEND)
471 ++ hmac.update(b_ciphertext)
472 ++ b_vaulttext = b'\n'.join([hexlify(b_salt), hexlify(hmac.finalize()), hexlify(b_ciphertext)])
473 + b_vaulttext = hexlify(b_vaulttext)
474 + return b_vaulttext
475 +
476 +@@ -757,48 +653,21 @@
477 + b_key1, b_key2, b_iv = self._gen_key_initctr(b_password, b_salt)
478 +
479 + # EXIT EARLY IF DIGEST DOESN'T MATCH
480 +- hmacDecrypt = HMAC.new(b_key2, b_ciphertext, SHA256)
481 +- if not self._is_equal(b_cryptedHmac, to_bytes(hmacDecrypt.hexdigest())):
482 ++ hmac = HMAC(b_key2, hashes.SHA256(), BACKEND)
483 ++ hmac.update(b_ciphertext)
484 ++ try:
485 ++ hmac.verify(unhexlify(b_cryptedHmac))
486 ++ except InvalidSignature:
487 + return None
488 +- # SET THE COUNTER AND THE CIPHER
489 +- ctr = Counter.new(128, initial_value=int(b_iv, 16))
490 +- cipher = AES.new(b_key1, AES.MODE_CTR, counter=ctr)
491 +-
492 +- # DECRYPT PADDED DATA
493 +- b_plaintext = cipher.decrypt(b_ciphertext)
494 +-
495 +- # UNPAD DATA
496 +- if PY3:
497 +- padding_length = b_plaintext[-1]
498 +- else:
499 +- padding_length = ord(b_plaintext[-1])
500 +
501 +- b_plaintext = b_plaintext[:-padding_length]
502 +- return b_plaintext
503 ++ cipher = C_Cipher(algorithms.AES(b_key1), modes.CTR(b_iv), BACKEND)
504 ++ decryptor = cipher.decryptor()
505 ++ unpadder = padding.PKCS7(128).unpadder()
506 ++ b_plaintext = unpadder.update(
507 ++ decryptor.update(b_ciphertext) + decryptor.finalize()
508 ++ ) + unpadder.finalize()
509 +
510 +- @staticmethod
511 +- def _is_equal(b_a, b_b):
512 +- """
513 +- Comparing 2 byte arrrays in constant time
514 +- to avoid timing attacks.
515 +-
516 +- It would be nice if there was a library for this but
517 +- hey.
518 +- """
519 +- if not (isinstance(b_a, binary_type) and isinstance(b_b, binary_type)):
520 +- raise TypeError('_is_equal can only be used to compare two byte strings')
521 +-
522 +- # http://codahale.com/a-lesson-in-timing-attacks/
523 +- if len(b_a) != len(b_b):
524 +- return False
525 +-
526 +- result = 0
527 +- for b_x, b_y in zip(b_a, b_b):
528 +- if PY3:
529 +- result |= b_x ^ b_y
530 +- else:
531 +- result |= ord(b_x) ^ ord(b_y)
532 +- return result == 0
533 ++ return b_plaintext
534 +
535 +
536 + # Keys could be made bytes later if the code that gets the data is more
537 +diff -uNr ansible-2.3.0.0.ORIG/test/runner/requirements/integration.txt ansible-2.3.0.0/test/runner/requirements/integration.txt
538 +--- ansible-2.3.0.0.ORIG/test/runner/requirements/integration.txt 2017-05-23 14:23:12.379595453 +0100
539 ++++ ansible-2.3.0.0/test/runner/requirements/integration.txt 2017-05-23 14:24:22.118598926 +0100
540 +@@ -1,8 +1,8 @@
541 ++cryptography
542 + jinja2
543 + jmespath
544 + junit-xml
545 + ordereddict ; python_version < '2.7'
546 + paramiko
547 + passlib
548 +-pycrypto
549 + pyyaml
550 +diff -uNr ansible-2.3.0.0.ORIG/test/runner/requirements/network-integration.txt ansible-2.3.0.0/test/runner/requirements/network-integration.txt
551 +--- ansible-2.3.0.0.ORIG/test/runner/requirements/network-integration.txt 2017-05-23 14:23:12.379595453 +0100
552 ++++ ansible-2.3.0.0/test/runner/requirements/network-integration.txt 2017-05-23 14:24:22.119598926 +0100
553 +@@ -1,5 +1,5 @@
554 ++cryptography
555 + jinja2
556 + junit-xml
557 + paramiko
558 +-pycrypto
559 + pyyaml
560 +diff -uNr ansible-2.3.0.0.ORIG/test/runner/requirements/sanity.txt ansible-2.3.0.0/test/runner/requirements/sanity.txt
561 +--- ansible-2.3.0.0.ORIG/test/runner/requirements/sanity.txt 2017-05-23 14:23:12.379595453 +0100
562 ++++ ansible-2.3.0.0/test/runner/requirements/sanity.txt 2017-05-23 14:26:01.910603896 +0100
563 +@@ -1,5 +1,7 @@
564 ++cryptography
565 + jinja2
566 + mock
567 ++paramiko
568 + pep8
569 + pylint
570 + pytest
571 +diff -uNr ansible-2.3.0.0.ORIG/test/runner/requirements/units.txt ansible-2.3.0.0/test/runner/requirements/units.txt
572 +--- ansible-2.3.0.0.ORIG/test/runner/requirements/units.txt 2017-05-23 14:23:12.379595453 +0100
573 ++++ ansible-2.3.0.0/test/runner/requirements/units.txt 2017-05-23 14:24:22.119598926 +0100
574 +@@ -1,10 +1,10 @@
575 + boto
576 + boto3
577 ++cryptography
578 + jinja2
579 + mock
580 + nose
581 + passlib
582 +-pycrypto
583 + pytest
584 + pytest-mock
585 + pytest-xdist
586 +diff -uNr ansible-2.3.0.0.ORIG/test/runner/requirements/windows-integration.txt ansible-2.3.0.0/test/runner/requirements/windows-integration.txt
587 +--- ansible-2.3.0.0.ORIG/test/runner/requirements/windows-integration.txt 2017-05-23 14:23:12.379595453 +0100
588 ++++ ansible-2.3.0.0/test/runner/requirements/windows-integration.txt 2017-05-23 14:24:22.119598926 +0100
589 +@@ -1,4 +1,6 @@
590 ++cryptography
591 + jinja2
592 + junit-xml
593 ++paramiko
594 + pywinrm
595 + pyyaml
596 +diff -uNr ansible-2.3.0.0.ORIG/test/units/parsing/vault/test_vault_editor.py ansible-2.3.0.0/test/units/parsing/vault/test_vault_editor.py
597 +--- ansible-2.3.0.0.ORIG/test/units/parsing/vault/test_vault_editor.py 2017-05-23 14:23:12.324595450 +0100
598 ++++ ansible-2.3.0.0/test/units/parsing/vault/test_vault_editor.py 2017-05-23 14:24:22.120598926 +0100
599 +@@ -22,7 +22,6 @@
600 +
601 + import os
602 + import tempfile
603 +-from nose.plugins.skip import SkipTest
604 +
605 + from ansible.compat.tests import unittest
606 + from ansible.compat.tests.mock import patch
607 +@@ -32,27 +31,6 @@
608 + from ansible.module_utils._text import to_bytes, to_text
609 +
610 +
611 +-# Counter import fails for 2.0.1, requires >= 2.6.1 from pip
612 +-try:
613 +- from Crypto.Util import Counter
614 +- HAS_COUNTER = True
615 +-except ImportError:
616 +- HAS_COUNTER = False
617 +-
618 +-# KDF import fails for 2.0.1, requires >= 2.6.1 from pip
619 +-try:
620 +- from Crypto.Protocol.KDF import PBKDF2
621 +- HAS_PBKDF2 = True
622 +-except ImportError:
623 +- HAS_PBKDF2 = False
624 +-
625 +-# AES IMPORTS
626 +-try:
627 +- from Crypto.Cipher import AES as AES
628 +- HAS_AES = True
629 +-except ImportError:
630 +- HAS_AES = False
631 +-
632 + v10_data = """$ANSIBLE_VAULT;1.0;AES
633 + 53616c7465645f5fd0026926a2d415a28a2622116273fbc90e377225c12a347e1daf4456d36a77f9
634 + 9ad98d59f61d06a4b66718d855f16fb7bdfe54d1ec8aeaa4d06c2dc1fa630ae1846a029877f0eeb1
635 +@@ -423,9 +401,6 @@
636 +
637 + def test_decrypt_1_0(self):
638 + # Skip testing decrypting 1.0 files if we don't have access to AES, KDF or Counter.
639 +- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
640 +- raise SkipTest
641 +-
642 + v10_file = tempfile.NamedTemporaryFile(delete=False)
643 + with v10_file as f:
644 + f.write(to_bytes(v10_data))
645 +@@ -451,9 +426,6 @@
646 + assert fdata.strip() == "foo", "incorrect decryption of 1.0 file: %s" % fdata.strip()
647 +
648 + def test_decrypt_1_1(self):
649 +- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
650 +- raise SkipTest
651 +-
652 + v11_file = tempfile.NamedTemporaryFile(delete=False)
653 + with v11_file as f:
654 + f.write(to_bytes(v11_data))
655 +@@ -478,10 +450,6 @@
656 + assert fdata.strip() == "foo", "incorrect decryption of 1.0 file: %s" % fdata.strip()
657 +
658 + def test_rekey_migration(self):
659 +- # Skip testing rekeying files if we don't have access to AES, KDF or Counter.
660 +- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
661 +- raise SkipTest
662 +-
663 + v10_file = tempfile.NamedTemporaryFile(delete=False)
664 + with v10_file as f:
665 + f.write(to_bytes(v10_data))
666 +diff -uNr ansible-2.3.0.0.ORIG/test/units/parsing/vault/test_vault.py ansible-2.3.0.0/test/units/parsing/vault/test_vault.py
667 +--- ansible-2.3.0.0.ORIG/test/units/parsing/vault/test_vault.py 2017-05-23 14:23:12.324595450 +0100
668 ++++ ansible-2.3.0.0/test/units/parsing/vault/test_vault.py 2017-05-23 14:24:22.120598926 +0100
669 +@@ -38,28 +38,6 @@
670 + from ansible.module_utils._text import to_bytes, to_text
671 +
672 +
673 +-# Counter import fails for 2.0.1, requires >= 2.6.1 from pip
674 +-try:
675 +- from Crypto.Util import Counter
676 +- HAS_COUNTER = True
677 +-except ImportError:
678 +- HAS_COUNTER = False
679 +-
680 +-# KDF import fails for 2.0.1, requires >= 2.6.1 from pip
681 +-try:
682 +- from Crypto.Protocol.KDF import PBKDF2
683 +- HAS_PBKDF2 = True
684 +-except ImportError:
685 +- HAS_PBKDF2 = False
686 +-
687 +-# AES IMPORTS
688 +-try:
689 +- from Crypto.Cipher import AES as AES
690 +- HAS_AES = True
691 +-except ImportError:
692 +- HAS_AES = False
693 +-
694 +-
695 + class TestVaultIsEncrypted(unittest.TestCase):
696 + def test_bytes_not_encrypted(self):
697 + b_data = b"foobar"
698 +@@ -181,38 +159,6 @@
699 + self.assertIsInstance(b_key, six.binary_type)
700 + self.assertEqual(b_key, b_key_2)
701 +
702 +- def test_is_equal_is_equal(self):
703 +- self.assertTrue(self.vault_cipher._is_equal(b'abcdefghijklmnopqrstuvwxyz', b'abcdefghijklmnopqrstuvwxyz'))
704 +-
705 +- def test_is_equal_unequal_length(self):
706 +- self.assertFalse(self.vault_cipher._is_equal(b'abcdefghijklmnopqrstuvwxyz', b'abcdefghijklmnopqrstuvwx and sometimes y'))
707 +-
708 +- def test_is_equal_not_equal(self):
709 +- self.assertFalse(self.vault_cipher._is_equal(b'abcdefghijklmnopqrstuvwxyz', b'AbcdefghijKlmnopQrstuvwxZ'))
710 +-
711 +- def test_is_equal_empty(self):
712 +- self.assertTrue(self.vault_cipher._is_equal(b'', b''))
713 +-
714 +- def test_is_equal_non_ascii_equal(self):
715 +- utf8_data = to_bytes(u'私はガラスを食べられます。それは私を傷つけません。')
716 +- self.assertTrue(self.vault_cipher._is_equal(utf8_data, utf8_data))
717 +-
718 +- def test_is_equal_non_ascii_unequal(self):
719 +- utf8_data = to_bytes(u'私はガラスを食べられます。それは私を傷つけません。')
720 +- utf8_data2 = to_bytes(u'Pot să mănânc sticlă și ea nu mă rănește.')
721 +-
722 +- # Test for the len optimization path
723 +- self.assertFalse(self.vault_cipher._is_equal(utf8_data, utf8_data2))
724 +- # Test for the slower, char by char comparison path
725 +- self.assertFalse(self.vault_cipher._is_equal(utf8_data, utf8_data[:-1] + b'P'))
726 +-
727 +- def test_is_equal_non_bytes(self):
728 +- """ Anything not a byte string should raise a TypeError """
729 +- self.assertRaises(TypeError, self.vault_cipher._is_equal, u"One fish", b"two fish")
730 +- self.assertRaises(TypeError, self.vault_cipher._is_equal, b"One fish", u"two fish")
731 +- self.assertRaises(TypeError, self.vault_cipher._is_equal, 1, b"red fish")
732 +- self.assertRaises(TypeError, self.vault_cipher._is_equal, b"blue fish", 2)
733 +-
734 +
735 + class TestVaultLib(unittest.TestCase):
736 + def setUp(self):
737 +@@ -267,8 +213,6 @@
738 + self.assertEqual(self.v.b_version, b"9.9", msg="version was not properly set")
739 +
740 + def test_encrypt_decrypt_aes(self):
741 +- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
742 +- raise SkipTest
743 + self.v.cipher_name = u'AES'
744 + self.v.b_password = b'ansible'
745 + # AES encryption code has been removed, so this is old output for
746 +@@ -282,8 +226,6 @@
747 + self.assertEqual(b_plaintext, b"foobar", msg="decryption failed")
748 +
749 + def test_encrypt_decrypt_aes256(self):
750 +- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
751 +- raise SkipTest
752 + self.v.cipher_name = u'AES256'
753 + plaintext = u"foobar"
754 + b_vaulttext = self.v.encrypt(plaintext)
755 +@@ -292,8 +234,6 @@
756 + self.assertEqual(b_plaintext, b"foobar", msg="decryption failed")
757 +
758 + def test_encrypt_decrypt_aes256_existing_vault(self):
759 +- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
760 +- raise SkipTest
761 + self.v.cipher_name = u'AES256'
762 + b_orig_plaintext = b"Setec Astronomy"
763 + vaulttext = u'''$ANSIBLE_VAULT;1.1;AES256
764 +@@ -314,8 +254,6 @@
765 + # FIXME This test isn't working quite yet.
766 + raise SkipTest
767 +
768 +- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
769 +- raise SkipTest
770 + self.v.cipher_name = 'AES256'
771 + # plaintext = "Setec Astronomy"
772 + enc_data = '''$ANSIBLE_VAULT;1.1;AES256
773 +@@ -350,8 +288,6 @@
774 + self.v.decrypt(b_invalid_ciphertext)
775 +
776 + def test_encrypt_encrypted(self):
777 +- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
778 +- raise SkipTest
779 + self.v.cipher_name = u'AES'
780 + b_vaulttext = b"$ANSIBLE_VAULT;9.9;TEST\n%s" % hexlify(b"ansible")
781 + vaulttext = to_text(b_vaulttext, errors='strict')
782 +@@ -359,8 +295,6 @@
783 + self.assertRaises(errors.AnsibleError, self.v.encrypt, vaulttext)
784 +
785 + def test_decrypt_decrypted(self):
786 +- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
787 +- raise SkipTest
788 + plaintext = u"ansible"
789 + self.assertRaises(errors.AnsibleError, self.v.decrypt, plaintext)
790 +
791 +@@ -368,9 +302,6 @@
792 + self.assertRaises(errors.AnsibleError, self.v.decrypt, b_plaintext)
793 +
794 + def test_cipher_not_set(self):
795 +- # not setting the cipher should default to AES256
796 +- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
797 +- raise SkipTest
798 + plaintext = u"ansible"
799 + self.v.encrypt(plaintext)
800 + self.assertEquals(self.v.cipher_name, "AES256")
801 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/docker/centos6/Dockerfile ansible-2.3.0.0/test/utils/docker/centos6/Dockerfile
802 +--- ansible-2.3.0.0.ORIG/test/utils/docker/centos6/Dockerfile 2017-05-23 14:23:12.321595450 +0100
803 ++++ ansible-2.3.0.0/test/utils/docker/centos6/Dockerfile 2017-05-23 14:24:22.121598926 +0100
804 +@@ -9,6 +9,8 @@
805 + file \
806 + gcc \
807 + git \
808 ++ libffi \
809 ++ libffi-devel \
810 + make \
811 + mercurial \
812 + mysql \
813 +@@ -16,6 +18,7 @@
814 + mysql-server \
815 + openssh-clients \
816 + openssh-server \
817 ++ openssl-devel \
818 + python-coverage \
819 + python-devel \
820 + python-httplib2 \
821 +@@ -40,8 +43,6 @@
822 + && \
823 + yum clean all
824 +
825 +-RUN rpm -e --nodeps python-crypto && pip install --upgrade pycrypto
826 +-
827 + RUN /bin/sed -i -e 's/^\(Defaults\s*requiretty\)/#--- \1/' /etc/sudoers
828 + RUN mkdir /etc/ansible/
829 + RUN /bin/echo -e '[local]\nlocalhost ansible_connection=local' > /etc/ansible/hosts
830 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/docker/centos7/Dockerfile ansible-2.3.0.0/test/utils/docker/centos7/Dockerfile
831 +--- ansible-2.3.0.0.ORIG/test/utils/docker/centos7/Dockerfile 2017-05-23 14:23:12.321595450 +0100
832 ++++ ansible-2.3.0.0/test/utils/docker/centos7/Dockerfile 2017-05-23 14:24:22.121598926 +0100
833 +@@ -17,15 +17,20 @@
834 + bzip2 \
835 + dbus-python \
836 + file \
837 ++ gcc \
838 + git \
839 + iproute \
840 ++ libffi \
841 ++ libffi-devel \
842 + make \
843 + mariadb-server \
844 + mercurial \
845 + MySQL-python \
846 + openssh-clients \
847 + openssh-server \
848 ++ openssl-devel \
849 + python-coverage \
850 ++ python-devel \
851 + python-httplib2 \
852 + python-jinja2 \
853 + python-keyczar \
854 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/docker/fedora24/Dockerfile ansible-2.3.0.0/test/utils/docker/fedora24/Dockerfile
855 +--- ansible-2.3.0.0.ORIG/test/utils/docker/fedora24/Dockerfile 2017-05-23 14:23:12.321595450 +0100
856 ++++ ansible-2.3.0.0/test/utils/docker/fedora24/Dockerfile 2017-05-23 14:24:22.121598926 +0100
857 +@@ -17,18 +17,23 @@
858 + dbus-python \
859 + file \
860 + findutils \
861 ++ gcc \
862 + git \
863 + glibc-locale-source \
864 + iproute \
865 ++ libffi \
866 ++ libffi-devel \
867 + make \
868 + mariadb-server \
869 + mercurial \
870 + MySQL-python \
871 + openssh-clients \
872 + openssh-server \
873 ++ openssl-devel \
874 + procps \
875 + python2-dnf \
876 + python-coverage \
877 ++ python-devel \
878 + python-httplib2 \
879 + python-jinja2 \
880 + python-keyczar \
881 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/docker/fedora25/Dockerfile ansible-2.3.0.0/test/utils/docker/fedora25/Dockerfile
882 +--- ansible-2.3.0.0.ORIG/test/utils/docker/fedora25/Dockerfile 2017-05-23 14:23:12.321595450 +0100
883 ++++ ansible-2.3.0.0/test/utils/docker/fedora25/Dockerfile 2017-05-23 14:24:22.121598926 +0100
884 +@@ -20,12 +20,15 @@
885 + git \
886 + glibc-locale-source \
887 + iproute \
888 ++ libffi \
889 ++ libffi-devel \
890 + make \
891 + mariadb-server \
892 + mercurial \
893 + MySQL-python \
894 + openssh-clients \
895 + openssh-server \
896 ++ openssl-devel \
897 + procps \
898 + python2-dnf \
899 + python-coverage \
900 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/docker/opensuse42.1/Dockerfile ansible-2.3.0.0/test/utils/docker/opensuse42.1/Dockerfile
901 +--- ansible-2.3.0.0.ORIG/test/utils/docker/opensuse42.1/Dockerfile 2017-05-23 14:23:12.321595450 +0100
902 ++++ ansible-2.3.0.0/test/utils/docker/opensuse42.1/Dockerfile 2017-05-23 14:24:22.121598926 +0100
903 +@@ -21,6 +21,8 @@
904 + openssh \
905 + postgresql-server \
906 + python-coverage \
907 ++ python-cryptography \
908 ++ python-devel \
909 + python-httplib2 \
910 + python-jinja2 \
911 + python-keyczar \
912 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/docker/opensuse42.2/Dockerfile ansible-2.3.0.0/test/utils/docker/opensuse42.2/Dockerfile
913 +--- ansible-2.3.0.0.ORIG/test/utils/docker/opensuse42.2/Dockerfile 2017-05-23 14:23:12.321595450 +0100
914 ++++ ansible-2.3.0.0/test/utils/docker/opensuse42.2/Dockerfile 2017-05-23 14:24:22.122598926 +0100
915 +@@ -21,6 +21,7 @@
916 + openssh \
917 + postgresql-server \
918 + python-coverage \
919 ++ python-cryptography \
920 + python-httplib2 \
921 + python-jinja2 \
922 + python-keyczar \
923 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/docker/ubuntu1204/Dockerfile ansible-2.3.0.0/test/utils/docker/ubuntu1204/Dockerfile
924 +--- ansible-2.3.0.0.ORIG/test/utils/docker/ubuntu1204/Dockerfile 2017-05-23 14:23:12.321595450 +0100
925 ++++ ansible-2.3.0.0/test/utils/docker/ubuntu1204/Dockerfile 2017-05-23 14:24:22.122598926 +0100
926 +@@ -17,6 +17,8 @@
927 + gawk \
928 + gcc \
929 + git \
930 ++ libffi-dev \
931 ++ libssl-dev \
932 + libxml2-utils \
933 + locales \
934 + make \
935 +@@ -50,8 +52,6 @@
936 + && \
937 + apt-get clean
938 +
939 +-RUN pip install --upgrade pycrypto
940 +-
941 + # helpful things taken from the ubuntu-upstart Dockerfile:
942 + # https://github.com/tianon/dockerfiles/blob/4d24a12b54b75b3e0904d8a285900d88d3326361/sbin-init/ubuntu/upstart/14.04/Dockerfile
943 + ADD init-fake.conf /etc/init/fake-container-events.conf
944 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/docker/ubuntu1404/Dockerfile ansible-2.3.0.0/test/utils/docker/ubuntu1404/Dockerfile
945 +--- ansible-2.3.0.0.ORIG/test/utils/docker/ubuntu1404/Dockerfile 2017-05-23 14:23:12.321595450 +0100
946 ++++ ansible-2.3.0.0/test/utils/docker/ubuntu1404/Dockerfile 2017-05-23 14:24:22.122598926 +0100
947 +@@ -16,6 +16,8 @@
948 + fakeroot \
949 + gawk \
950 + git \
951 ++ libffi-dev \
952 ++ libssl-dev \
953 + libxml2-utils \
954 + locales \
955 + make \
956 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/docker/ubuntu1604/Dockerfile ansible-2.3.0.0/test/utils/docker/ubuntu1604/Dockerfile
957 +--- ansible-2.3.0.0.ORIG/test/utils/docker/ubuntu1604/Dockerfile 2017-05-23 14:23:12.321595450 +0100
958 ++++ ansible-2.3.0.0/test/utils/docker/ubuntu1604/Dockerfile 2017-05-23 14:24:22.122598926 +0100
959 +@@ -17,6 +17,8 @@
960 + gawk \
961 + git \
962 + iproute2 \
963 ++ libffi-dev \
964 ++ libssl-dev \
965 + libxml2-utils \
966 + locales \
967 + lsb-release \
968 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/docker/ubuntu1604py3/Dockerfile ansible-2.3.0.0/test/utils/docker/ubuntu1604py3/Dockerfile
969 +--- ansible-2.3.0.0.ORIG/test/utils/docker/ubuntu1604py3/Dockerfile 2017-05-23 14:23:12.321595450 +0100
970 ++++ ansible-2.3.0.0/test/utils/docker/ubuntu1604py3/Dockerfile 2017-05-23 14:24:22.122598926 +0100
971 +@@ -15,6 +15,8 @@
972 + gawk \
973 + git \
974 + iproute2 \
975 ++ libffi-dev \
976 ++ libssl-dev \
977 + libxml2-utils \
978 + locales \
979 + lsb-release \
980 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/shippable/other.sh ansible-2.3.0.0/test/utils/shippable/other.sh
981 +--- ansible-2.3.0.0.ORIG/test/utils/shippable/other.sh 2017-05-23 14:23:12.320595450 +0100
982 ++++ ansible-2.3.0.0/test/utils/shippable/other.sh 2017-05-23 14:24:22.123598926 +0100
983 +@@ -12,6 +12,8 @@
984 + retry.py apt-get install -qq \
985 + shellcheck \
986 + python2.4 \
987 ++ libssl-dev \
988 ++ libffi-dev \
989 +
990 + ln -sf x86_64-linux-gnu-gcc-4.9 /usr/bin/x86_64-linux-gnu-gcc
991 +
992 +diff -uNr ansible-2.3.0.0.ORIG/test/utils/tox/requirements.txt ansible-2.3.0.0/test/utils/tox/requirements.txt
993 +--- ansible-2.3.0.0.ORIG/test/utils/tox/requirements.txt 2017-05-23 14:23:12.321595450 +0100
994 ++++ ansible-2.3.0.0/test/utils/tox/requirements.txt 2017-05-23 14:24:22.123598926 +0100
995 +@@ -11,7 +11,7 @@
996 + redis
997 + python-memcached
998 + python-systemd
999 +-pycrypto
1000 ++cryptography
1001 + botocore
1002 + boto3
1003 + pytest