Files
trove/trove/tests/unittests/common/test_crypto_utils.py
T
Victor Stinner f9ed13a576 Port crypto_utils to Python 3
This change is backward compatible on Python 2:

* Data encrypted with the old code can be decrypted with the new code
* Data encrypted with the new code can be decrypted with the old code

Effect on Python 2: it now is possible to encode Unicode strings
which contain non-ASCII characters. Before encode_data() failed with
UnicodeEncodeError.

Changes:

* pad_for_encryption(): replace chr() with six.int2byte() to get
  bytes on Python 3.
* use byte strings in tests
* encode_data() now encodes data to UTF-8 (if it is Unicode)
* encrypt() and decrypt() now encode the encryption key to UTF-8 (if
  it's Unicode)
* encrypt() now encodes data to UTF-8 (if it's Unicode), it's only
  needed on Python 3 since encode_data() returns Unicode on Python 3.
* test_encode_decode_string(): test also Unicode
* Add new unit tests on encrypt() and decrypt() with known values and
  hardcoded IV.
* tox.ini: add test_common_extensions and test_crypto_utils
  to Python 3.4 (test_common_extensions already passed before).

Note: UTF-8 is the defacto standard encoding in OpenStack. It is
already used widely in all parts of OpenStack.

Partially implements: blueprint trove-python3
Change-Id: I8e991994698d558177580595cf2c7da93ed1e1c8
2016-04-20 14:45:26 +02:00

113 lines
4.3 KiB
Python

# Copyright 2016 Tesora, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from Crypto import Random
import mock
import six
from trove.common import crypto_utils
from trove.tests.unittests import trove_testtools
class TestEncryptUtils(trove_testtools.TestCase):
def setUp(self):
super(TestEncryptUtils, self).setUp()
def tearDown(self):
super(TestEncryptUtils, self).tearDown()
def test_encode_decode_string(self):
random_data = bytearray(Random.new().read(12))
data = [b'abc', b'numbers01234', b'\x00\xFF\x00\xFF\xFF\x00',
random_data, u'Unicode:\u20ac']
for datum in data:
encoded_data = crypto_utils.encode_data(datum)
decoded_data = crypto_utils.decode_data(encoded_data)
if isinstance(datum, six.text_type):
decoded_data = decoded_data.decode('utf-8')
self. assertEqual(datum, decoded_data,
"Encode/decode failed")
def test_pad_unpad(self):
for size in range(1, 100):
data_str = b'a' * size
padded_str = crypto_utils.pad_for_encryption(
data_str, crypto_utils.IV_BIT_COUNT)
self.assertEqual(0, len(padded_str) % crypto_utils.IV_BIT_COUNT,
"Padding not successful")
unpadded_str = crypto_utils.unpad_after_decryption(padded_str)
self.assertEqual(data_str, unpadded_str,
"String mangled after pad/unpad")
def test_encryp_decrypt(self):
key = 'my_secure_key'
for size in range(1, 100):
orig_data = Random.new().read(size)
orig_encoded = crypto_utils.encode_data(orig_data)
encrypted = crypto_utils.encrypt_data(orig_encoded, key)
encoded = crypto_utils.encode_data(encrypted)
decoded = crypto_utils.decode_data(encoded)
decrypted = crypto_utils.decrypt_data(decoded, key)
final_decoded = crypto_utils.decode_data(decrypted)
self.assertEqual(orig_data, final_decoded,
"Decrypted data did not match original")
def test_encrypt(self):
# test encrypt() with an hardcoded IV
key = 'my_secure_key'
salt = b'x' * crypto_utils.IV_BIT_COUNT
with mock.patch('Crypto.Random.new') as mock_random:
mock_random.return_value.read.return_value = salt
for orig_data, expected in (
# byte string
(b'Hello World!',
'eHh4eHh4eHh4eHh4eHh4eF5RK6VdDrAWl4Th1mNG2eps+VB2BouFRiY2Wa'
'P/RRPT'),
# Unicoded string (encoded to UTF-8)
(u'Unicode:\u20ac',
'eHh4eHh4eHh4eHh4eHh4eAMsI5YsrtMNAPJfVF0j9NegXML7OsJ0LuAy66'
'LKv5F4'),
):
orig_encoded = crypto_utils.encode_data(orig_data)
encrypted = crypto_utils.encrypt_data(orig_encoded, key)
encoded = crypto_utils.encode_data(encrypted)
self.assertEqual(expected, encoded)
def test_decrypt(self):
key = 'my_secure_key'
for encoded, expected in (
# byte string: b'Hello World!'
('ZUhoNGVIaDRlSGg0ZUhoNL9PmM70hVcQ7j/kYF7Pw+BT7VSfsht0VsCIxy'
'KNN0NH',
b'Hello World!'),
# Unicoded string: u'Unicode:\u20ac'
('ZUhoNGVIaDRlSGg0ZUhoNIHZLIuIcQCRwWY7PR2y7JcqoDf4ViqXIfh0uE'
'Rbg9BA',
b'Unicode:\xe2\x82\xac'),
):
decoded = crypto_utils.decode_data(encoded)
decrypted = crypto_utils.decrypt_data(decoded, key)
final_decoded = crypto_utils.decode_data(decrypted)
self.assertEqual(expected, final_decoded)