2
0
mirror of https://github.com/openkmip/pykmip synced 2025-12-28 06:03:22 +00:00

Add decryption support to the server cryptography engine

This change adds decryption functionality to the cryptographic
engine used by the server. It supports a variety of symmetric
decryption algorithms and block cipher modes. Asymmetric
decryption support will be added in a future patch.

Unit tests and minor updates to surrounding code are included.
This commit is contained in:
Peter Hamilton
2017-06-27 14:05:28 -04:00
parent e18dd54599
commit 60bd56066e
3 changed files with 307 additions and 5 deletions

View File

@@ -379,6 +379,101 @@ class TestCryptographyEngine(testtools.TestCase):
self.assertNotIn('iv_nonce', result.keys())
def test_decrypt_invalid_algorithm(self):
"""
Test that the right errors are raised when invalid decryption
algorithms are used.
"""
engine = crypto.CryptographyEngine()
args = (None, b'', b'')
self.assertRaisesRegexp(
exceptions.InvalidField,
"Decryption algorithm is required.",
engine.decrypt,
*args
)
args = ('invalid', b'', b'')
self.assertRaisesRegexp(
exceptions.InvalidField,
"Decryption algorithm 'invalid' is not a supported symmetric "
"decryption algorithm.",
engine.decrypt,
*args
)
def test_decrypt_invalid_algorithm_key(self):
"""
Test that the right error is raised when an invalid key is used with
a decryption algorithm.
"""
engine = crypto.CryptographyEngine()
args = (enums.CryptographicAlgorithm.AES, b'', b'')
self.assertRaisesRegexp(
exceptions.CryptographicFailure,
"Invalid key bytes for the specified decryption algorithm.",
engine.decrypt,
*args
)
def test_decrypt_invalid_cipher_mode(self):
"""
Test that the right errors are raised when invalid cipher modes are
used.
"""
engine = crypto.CryptographyEngine()
args = (
enums.CryptographicAlgorithm.AES,
b'\x00\x01\x02\x03\x04\x05\x06\x07'
b'\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F',
b'\x0F\x0E\x0D\x0C\x0B\x0A\x09\x08'
b'\x07\x06\x05\x04\x03\x02\x01\x00'
)
self.assertRaisesRegexp(
exceptions.InvalidField,
"Cipher mode is required.",
engine.decrypt,
*args
)
kwargs = {'cipher_mode': 'invalid'}
self.assertRaisesRegexp(
exceptions.InvalidField,
"Cipher mode 'invalid' is not a supported mode.",
engine.decrypt,
*args,
**kwargs
)
def test_decrypt_missing_iv_nonce(self):
"""
Test that the right error is raised when an IV/nonce is not provided
for the decryption algorithm.
"""
engine = crypto.CryptographyEngine()
args = (
enums.CryptographicAlgorithm.AES,
b'\x00\x01\x02\x03\x04\x05\x06\x07'
b'\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F',
b'\x0F\x0E\x0D\x0C\x0B\x0A\x09\x08'
b'\x07\x06\x05\x04\x03\x02\x01\x00'
)
kwargs = {
'cipher_mode': enums.BlockCipherMode.CBC,
'padding_method': enums.PaddingMethod.PKCS5
}
self.assertRaisesRegexp(
exceptions.InvalidField,
"IV/nonce is required.",
engine.decrypt,
*args,
**kwargs
)
def test_handle_symmetric_padding_invalid(self):
"""
Test that the right errors are raised when invalid padding methods
@@ -611,20 +706,51 @@ def test_encrypt(encrypt_parameters):
assert encrypt_parameters.get('cipher_text') == result.get('cipher_text')
def test_decrypt(encrypt_parameters):
"""
Test that various decryption algorithms and block cipher modes can be
used to correctly decrypt data.
"""
engine = crypto.CryptographyEngine()
engine._handle_symmetric_padding = mock.MagicMock(
return_value=encrypt_parameters.get('plain_text')
)
result = engine.decrypt(
encrypt_parameters.get('algorithm'),
encrypt_parameters.get('key'),
encrypt_parameters.get('cipher_text'),
cipher_mode=encrypt_parameters.get('cipher_mode'),
iv_nonce=encrypt_parameters.get('iv_nonce')
)
if engine._handle_symmetric_padding.called:
engine._handle_symmetric_padding.assert_called_once_with(
engine._symmetric_key_algorithms.get(
encrypt_parameters.get('algorithm')
),
encrypt_parameters.get('plain_text'),
None
)
assert encrypt_parameters.get('plain_text') == result
@pytest.fixture(
scope='function',
params=[
{'algorithm': algorithms.AES,
'plain_text': b'\x48\x65\x6C\x6C\x6F',
'padding_method': enums.PaddingMethod.PKCS5,
'result': (
'padded_text': (
b'\x48\x65\x6C\x6C\x6F\x0B\x0B\x0B'
b'\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B'
)},
{'algorithm': algorithms.TripleDES,
'plain_text': b'\x48\x65\x6C\x6C\x6F',
'padding_method': enums.PaddingMethod.ANSI_X923,
'result': b'\x48\x65\x6C\x6C\x6F\x00\x00\x03'}
'padded_text': b'\x48\x65\x6C\x6C\x6F\x00\x00\x03'}
]
)
def symmetric_padding_parameters(request):
@@ -644,4 +770,21 @@ def test_handle_symmetric_padding(symmetric_padding_parameters):
symmetric_padding_parameters.get('padding_method')
)
assert result == symmetric_padding_parameters.get('result')
assert result == symmetric_padding_parameters.get('padded_text')
def test_handle_symmetric_padding_undo(symmetric_padding_parameters):
"""
Test that data of various lengths can be unpadded correctly using
different padding schemes.
"""
engine = crypto.CryptographyEngine()
result = engine._handle_symmetric_padding(
symmetric_padding_parameters.get('algorithm'),
symmetric_padding_parameters.get('padded_text'),
symmetric_padding_parameters.get('padding_method'),
undo_padding=True
)
assert result == symmetric_padding_parameters.get('plain_text')