summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVilmos Nebehaj <v.nebehaj@gmail.com>2015-07-22 19:52:42 +0200
committerVilmos Nebehaj <v.nebehaj@gmail.com>2015-07-28 14:51:36 +0200
commit58cccce3843031dc073533e0028ca929ae047817 (patch)
treeb374df8452b782b64b54cee5eb783a32255b8cc5
parente505a1b7c4f04fc97e7abf2e7d196f3297f25e61 (diff)
downloadansible-58cccce3843031dc073533e0028ca929ae047817.tar.gz
Use PBKDF2HMAC() from cryptography for vault keys.
When stretching the key for vault files, use PBKDF2HMAC() from the cryptography package instead of pycrypto. This will speed up the opening of vault files by ~10x. The problem is here in lib/ansible/utils/vault.py: hash_function = SHA256 # make two keys and one iv pbkdf2_prf = lambda p, s: HMAC.new(p, s, hash_function).digest() derivedkey = PBKDF2(password, salt, dkLen=(2 * keylength) + ivlength, count=10000, prf=pbkdf2_prf) `PBKDF2()` calls a Python callback function (`pbkdf2_pr()`) 10000 times. If one has several vault files, this will cause excessive start times with `ansible` or `ansible-playbook` (we experience ~15 second startup times). Testing the original implementation in 1.9.2 with a vault file: In [2]: %timeit v.decrypt(encrypted_data) 1 loops, best of 3: 265 ms per loop Having a recent OpenSSL version and using the vault.py changes in this commit: In [2]: %timeit v.decrypt(encrypted_data) 10 loops, best of 3: 23.2 ms per loop
-rw-r--r--lib/ansible/parsing/vault/__init__.py42
1 files changed, 34 insertions, 8 deletions
diff --git a/lib/ansible/parsing/vault/__init__.py b/lib/ansible/parsing/vault/__init__.py
index f3cee27ea4..6df786a212 100644
--- a/lib/ansible/parsing/vault/__init__.py
+++ b/lib/ansible/parsing/vault/__init__.py
@@ -81,6 +81,18 @@ try:
except ImportError:
HAS_AES = False
+# OpenSSL pbkdf2_hmac
+HAS_PBKDF2HMAC = False
+try:
+ from cryptography.hazmat.primitives.hashes import SHA256 as c_SHA256
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
+ from cryptography.hazmat.backends import default_backend
+ HAS_PBKDF2HMAC = True
+except ImportError:
+ pass
+
+HAS_ANY_PBKDF2HMAC = HAS_PBKDF2 or HAS_PBKDF2HMAC
+
CRYPTO_UPGRADE = "ansible-vault requires a newer version of pycrypto than the one installed on your platform. You may fix this with OS-specific commands such as: yum install python-devel; rpm -e --nodeps python-crypto; pip install pycrypto"
HEADER=u'$ANSIBLE_VAULT'
@@ -89,7 +101,7 @@ CIPHER_WHITELIST=['AES', 'AES256']
def check_prereqs():
- if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2 or not HAS_HASH:
+ if not HAS_AES or not HAS_COUNTER or not HAS_ANY_PBKDF2HMAC or not HAS_HASH:
raise AnsibleError(CRYPTO_UPGRADE)
class VaultLib(object):
@@ -551,13 +563,7 @@ class VaultAES256(object):
check_prereqs()
- def gen_key_initctr(self, password, salt):
- # 16 for AES 128, 32 for AES256
- keylength = 32
-
- # match the size used for counter.new to avoid extra work
- ivlength = 16
-
+ def create_key(self, password, salt, keylength, ivlength):
hash_function = SHA256
# make two keys and one iv
@@ -566,6 +572,26 @@ class VaultAES256(object):
derivedkey = PBKDF2(password, salt, dkLen=(2 * keylength) + ivlength,
count=10000, prf=pbkdf2_prf)
+ return derivedkey
+
+ def gen_key_initctr(self, password, salt):
+ # 16 for AES 128, 32 for AES256
+ keylength = 32
+
+ # match the size used for counter.new to avoid extra work
+ ivlength = 16
+
+ if HAS_PBKDF2HMAC:
+ backend = default_backend()
+ kdf = PBKDF2HMAC(
+ algorithm=c_SHA256(),
+ length=2 * keylength + ivlength,
+ salt=salt,
+ iterations=10000,
+ backend=backend)
+ derivedkey = kdf.derive(password)
+ else:
+ derivedkey = self.create_key(password, salt, keylength, ivlength)
key1 = derivedkey[:keylength]
key2 = derivedkey[keylength:(keylength * 2)]