Use cryptography instead of pycrypto

I was following up on [0] to see how much work would be required to
get Glance off pycrypto.  There's not much, so I decided to just do
it.  This patch rewrites the utility functions that were using the
pycrpyto library to instead use cryptography and removes pycrypto
from requirements.txt.

Note: PS1 included both the pycrypto- and cryptography- based
utilities in order to show that the new utilities can decrypt output
from the old utilities, and the old utilities can decrypt output from
the new utilities (needed for rolling upgrades).  You can look at the
earlier test results or download PS1 to run and verify.

[0] http://lists.openstack.org/pipermail/openstack-dev/2017-March/113568.html

Change-Id: Ib95644747e20bfd8ade5572d46651f9bd706e9da
This commit is contained in:
Brian Rosmaita 2017-03-23 22:17:29 -04:00
parent 9de043acb0
commit 5ebde9079b
2 changed files with 22 additions and 11 deletions

View File

@ -18,10 +18,13 @@ Routines for URL-safe encrypting/decrypting
"""
import base64
import os
import random
from Crypto.Cipher import AES
from Crypto import Random
from Crypto.Random import random
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import algorithms
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers import modes
from oslo_utils import encodeutils
import six
# NOTE(jokke): simplified transition to py3, behaves like py2 xrange
@ -44,8 +47,10 @@ def urlsafe_encrypt(key, plaintext, blocksize=16):
Pads text to be encrypted
"""
pad_length = (blocksize - len(text) % blocksize)
sr = random.StrongRandom()
pad = b''.join(six.int2byte(sr.randint(1, 0xFF))
# NOTE(rosmaita): I know this looks stupid, but we can't just
# use os.urandom() to get the bytes because we use char(0) as
# a delimiter
pad = b''.join(six.int2byte(random.SystemRandom().randint(1, 0xFF))
for i in range(pad_length - 1))
# We use chr(0) as a delimiter between text and padding
return text + b'\0' + pad
@ -53,9 +58,13 @@ def urlsafe_encrypt(key, plaintext, blocksize=16):
plaintext = encodeutils.to_utf8(plaintext)
key = encodeutils.to_utf8(key)
# random initial 16 bytes for CBC
init_vector = Random.get_random_bytes(16)
cypher = AES.new(key, AES.MODE_CBC, init_vector)
padded = cypher.encrypt(pad(six.binary_type(plaintext)))
init_vector = os.urandom(16)
backend = default_backend()
cypher = Cipher(algorithms.AES(key), modes.CBC(init_vector),
backend=backend)
encryptor = cypher.encryptor()
padded = encryptor.update(
pad(six.binary_type(plaintext))) + encryptor.finalize()
encoded = base64.urlsafe_b64encode(init_vector + padded)
if six.PY3:
encoded = encoded.decode('ascii')
@ -76,8 +85,11 @@ def urlsafe_decrypt(key, ciphertext):
ciphertext = encodeutils.to_utf8(ciphertext)
key = encodeutils.to_utf8(key)
ciphertext = base64.urlsafe_b64decode(ciphertext)
cypher = AES.new(key, AES.MODE_CBC, ciphertext[:16])
padded = cypher.decrypt(ciphertext[16:])
backend = default_backend()
cypher = Cipher(algorithms.AES(key), modes.CBC(ciphertext[:16]),
backend=backend)
decryptor = cypher.decryptor()
padded = decryptor.update(ciphertext[16:]) + decryptor.finalize()
text = padded[:padded.rfind(b'\0')]
if six.PY3:
text = text.decode('utf-8')

View File

@ -15,7 +15,6 @@ sqlalchemy-migrate>=0.9.6 # Apache-2.0
sqlparse>=0.2.2 # BSD
alembic>=0.8.10 # MIT
httplib2>=0.7.5 # MIT
pycrypto>=2.6 # Public Domain
oslo.config>=3.22.0 # Apache-2.0
oslo.concurrency>=3.8.0 # Apache-2.0
oslo.context>=2.12.0 # Apache-2.0