Merge "Update crypto plugin interface to support Dogtag"
This commit is contained in:
commit
e4e7ce18c9
@ -245,11 +245,12 @@ class CryptoExtensionManager(named.NamedExtensionManager):
|
||||
kek_datum, kek_meta_dto = self._find_or_create_kek_objects(
|
||||
encrypting_plugin, tenant, kek_repo)
|
||||
|
||||
encrypt_dto = plugin_mod.EncryptDTO(unencrypted)
|
||||
# Create an encrypted datum instance and add the encrypted cypher text.
|
||||
datum = models.EncryptedDatum(secret, kek_datum)
|
||||
datum.content_type = content_type
|
||||
datum.cypher_text, datum.kek_meta_extended = encrypting_plugin.encrypt(
|
||||
unencrypted, kek_meta_dto, tenant.keystone_id
|
||||
encrypt_dto, kek_meta_dto, tenant.keystone_id
|
||||
)
|
||||
|
||||
# Convert binary data into a text-based format.
|
||||
@ -279,10 +280,11 @@ class CryptoExtensionManager(named.NamedExtensionManager):
|
||||
#TODO(jwood) Figure out by storing binary (BYTEA) data in
|
||||
# Postgres isn't working.
|
||||
encrypted = base64.b64decode(datum.cypher_text)
|
||||
decrypt_dto = plugin_mod.DecryptDTO(encrypted)
|
||||
|
||||
# Decrypt the secret.
|
||||
unencrypted = decrypting_plugin \
|
||||
.decrypt(encrypted,
|
||||
.decrypt(decrypt_dto,
|
||||
kek_meta_dto,
|
||||
datum.kek_meta_extended,
|
||||
tenant.keystone_id)
|
||||
@ -313,16 +315,28 @@ class CryptoExtensionManager(named.NamedExtensionManager):
|
||||
else:
|
||||
raise CryptoSupportedPluginNotFound()
|
||||
|
||||
# Create the secret.
|
||||
kek_datum, kek_meta_dto = self._find_or_create_kek_objects(
|
||||
encrypting_plugin, tenant, kek_repo)
|
||||
|
||||
data_key = encrypting_plugin.create(secret.bit_length,
|
||||
generation_type,
|
||||
secret.algorithm,
|
||||
secret.mode)
|
||||
# Create an encrypted datum instance and add the created cypher text.
|
||||
datum = models.EncryptedDatum(secret, kek_datum)
|
||||
datum.content_type = content_type
|
||||
|
||||
# Encrypt the secret.
|
||||
return self.encrypt(data_key, content_type, None, secret, tenant,
|
||||
kek_repo)
|
||||
generate_dto = plugin_mod.GenerateDTO(generation_type,
|
||||
secret.algorithm,
|
||||
secret.bit_length,
|
||||
secret.mode)
|
||||
# Create the encrypted secret.
|
||||
datum.cypher_text, datum.kek_meta_extended =\
|
||||
encrypting_plugin.generate(
|
||||
generate_dto, kek_meta_dto, tenant.keystone_id)
|
||||
|
||||
# Convert binary data into a text-based format.
|
||||
# TODO(jwood) Figure out by storing binary (BYTEA) data in Postgres
|
||||
# isn't working.
|
||||
datum.cypher_text = base64.b64encode(datum.cypher_text)
|
||||
|
||||
return datum
|
||||
|
||||
def _determine_type(self, algorithm):
|
||||
"""Determines the type (symmetric only for now) based on algorithm"""
|
||||
|
@ -124,12 +124,12 @@ class P11CryptoPlugin(plugin.CryptoPluginBase):
|
||||
)
|
||||
)
|
||||
|
||||
def encrypt(self, unencrypted, kek_meta_dto, keystone_id):
|
||||
def encrypt(self, encrypt_dto, kek_meta_dto, keystone_id):
|
||||
key = self._get_key_by_label(kek_meta_dto.kek_label)
|
||||
iv = self._generate_iv()
|
||||
gcm = self._build_gcm_params(iv)
|
||||
mech = PyKCS11.Mechanism(self.algorithm, gcm)
|
||||
encrypted = self.session.encrypt(key, unencrypted, mech)
|
||||
encrypted = self.session.encrypt(key, encrypt_dto.unencrypted, mech)
|
||||
cyphertext = b''.join(chr(i) for i in encrypted)
|
||||
kek_meta_extended = json.dumps({
|
||||
'iv': base64.b64encode(iv)
|
||||
@ -137,13 +137,14 @@ class P11CryptoPlugin(plugin.CryptoPluginBase):
|
||||
|
||||
return cyphertext, kek_meta_extended
|
||||
|
||||
def decrypt(self, encrypted, kek_meta_dto, kek_meta_extended, keystone_id):
|
||||
def decrypt(self, decrypt_dto, kek_meta_dto, kek_meta_extended,
|
||||
keystone_id):
|
||||
key = self._get_key_by_label(kek_meta_dto.kek_label)
|
||||
meta_extended = json.loads(kek_meta_extended)
|
||||
iv = base64.b64decode(meta_extended['iv'])
|
||||
gcm = self._build_gcm_params(iv)
|
||||
mech = PyKCS11.Mechanism(self.algorithm, gcm)
|
||||
decrypted = self.session.decrypt(key, encrypted, mech)
|
||||
decrypted = self.session.decrypt(key, decrypt_dto.encrypted, mech)
|
||||
secret = b''.join(chr(i) for i in decrypted)
|
||||
return secret
|
||||
|
||||
@ -161,12 +162,12 @@ class P11CryptoPlugin(plugin.CryptoPluginBase):
|
||||
|
||||
return kek_meta_dto
|
||||
|
||||
def create(self, bit_length, type_enum, algorithm=None, mode=None):
|
||||
byte_length = bit_length / 8
|
||||
def generate(self, generate_dto, kek_meta_dto, keystone_id):
|
||||
byte_length = generate_dto.bit_length / 8
|
||||
rand = self.session.generateRandom(byte_length)
|
||||
if len(rand) != byte_length:
|
||||
raise P11CryptoPluginException()
|
||||
return rand
|
||||
return self.encrypt(plugin.EncryptDTO(rand), kek_meta_dto, keystone_id)
|
||||
|
||||
def supports(self, type_enum, algorithm=None, mode=None):
|
||||
if type_enum == plugin.PluginSupportTypes.ENCRYPT_DECRYPT:
|
||||
|
@ -64,6 +64,45 @@ class KEKMetaDTO(object):
|
||||
self.plugin_meta = kek_datum.plugin_meta
|
||||
|
||||
|
||||
class GenerateDTO(object):
|
||||
"""
|
||||
Data transfer object for secret generation.
|
||||
|
||||
All data needed to determine the kinds of secrets to be generated
|
||||
is passed in instances of this object.
|
||||
"""
|
||||
|
||||
def __init__(self, generation_type, algorithm, bit_length, mode):
|
||||
self.generation_type = generation_type
|
||||
self.algorithm = algorithm
|
||||
self.bit_length = bit_length
|
||||
self.mode = mode
|
||||
|
||||
|
||||
class DecryptDTO(object):
|
||||
"""
|
||||
Data Transfer object for secret decryption.
|
||||
|
||||
All data needed to determine the kinds of secrets to be decrypted
|
||||
is passed in instances of this object.
|
||||
"""
|
||||
|
||||
def __init__(self, encrypted):
|
||||
self.encrypted = encrypted
|
||||
|
||||
|
||||
class EncryptDTO(object):
|
||||
"""
|
||||
Data Transfer object for secret encryption.
|
||||
|
||||
All data needed to determine the kinds of secrets to be encrypted
|
||||
is passed in instances of this object.
|
||||
"""
|
||||
|
||||
def __init__(self, unencrypted):
|
||||
self.unencrypted = unencrypted
|
||||
|
||||
|
||||
def indicate_bind_completed(kek_meta_dto, kek_datum):
|
||||
"""
|
||||
Updates the supplied kek_datum instance per the contents of the supplied
|
||||
@ -88,10 +127,11 @@ class CryptoPluginBase(object):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@abc.abstractmethod
|
||||
def encrypt(self, unencrypted, kek_meta_dto, keystone_id):
|
||||
def encrypt(self, encrypt_dto, kek_meta_dto, keystone_id):
|
||||
"""Encrypt unencrypted data in the context of the provided tenant.
|
||||
|
||||
:param unencrypted: byte data to be encrypted.
|
||||
:param encrypt_dto: data transfer object containing the byte data
|
||||
to be encrypted.
|
||||
:param kek_meta_dto: Key encryption key metadata to use for encryption.
|
||||
:param keystone_id: keystone_id associated with the unencrypted data.
|
||||
:returns: encrypted data and kek_meta_extended, the former the
|
||||
@ -103,10 +143,12 @@ class CryptoPluginBase(object):
|
||||
raise NotImplementedError # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def decrypt(self, encrypted, kek_meta_dto, kek_meta_extended, keystone_id):
|
||||
def decrypt(self, decrypt_dto, kek_meta_dto, kek_meta_extended,
|
||||
keystone_id):
|
||||
"""Decrypt encrypted_datum in the context of the provided tenant.
|
||||
|
||||
:param encrypted: cyphertext to be decrypted.
|
||||
:param decrypt_dto: data transfer object containing the cyphertext
|
||||
to be decrypted.
|
||||
:param kek_meta_dto: Key encryption key metadata to use for decryption
|
||||
:param kek_meta_extended: Optional per-secret KEK metadata to use for
|
||||
decryption.
|
||||
@ -139,8 +181,21 @@ class CryptoPluginBase(object):
|
||||
raise NotImplementedError # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def create(self, bit_length, type_enum, algorithm=None, mode=None):
|
||||
"""Create a new key."""
|
||||
def generate(self, generate_dto, kek_meta_dto, keystone_id):
|
||||
"""
|
||||
Generate a new key.
|
||||
|
||||
:param generate_dto: data transfer object for the record
|
||||
associated with this generation request. Some relevant
|
||||
parameters can be extracted from this object, including
|
||||
bit_length, algorithm and mode
|
||||
:param kek_meta_dto: Key encryption key metadata to use for decryption
|
||||
:param keystone_id: keystone_id associated with the data.
|
||||
:returns: encrypted data and kek_meta_extended, the former the
|
||||
resultant cypher text, the latter being optional per-secret metadata
|
||||
needed to decrypt (over and above the per-tenant metadata managed
|
||||
outside of the plugins)
|
||||
"""
|
||||
raise NotImplementedError # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
@ -172,7 +227,8 @@ class SimpleCryptoPlugin(CryptoPluginBase):
|
||||
unpadded = unencrypted[:-pad_length]
|
||||
return unpadded
|
||||
|
||||
def encrypt(self, unencrypted, kek_meta_dto, keystone_id):
|
||||
def encrypt(self, encrypt_dto, kek_meta_dto, keystone_id):
|
||||
unencrypted = encrypt_dto.unencrypted
|
||||
if not isinstance(unencrypted, str):
|
||||
raise ValueError('Unencrypted data must be a byte type, '
|
||||
'but was {0}'.format(type(unencrypted)))
|
||||
@ -184,7 +240,9 @@ class SimpleCryptoPlugin(CryptoPluginBase):
|
||||
|
||||
return cyphertext, None
|
||||
|
||||
def decrypt(self, encrypted, kek_meta_dto, kek_meta_extended, keystone_id):
|
||||
def decrypt(self, encrypted_dto, kek_meta_dto, kek_meta_extended,
|
||||
keystone_id):
|
||||
encrypted = encrypted_dto.encrypted
|
||||
iv = encrypted[:self.block_size]
|
||||
cypher_text = encrypted[self.block_size:]
|
||||
decryptor = AES.new(self.kek, AES.MODE_CBC, iv)
|
||||
@ -198,9 +256,10 @@ class SimpleCryptoPlugin(CryptoPluginBase):
|
||||
kek_meta_dto.plugin_meta = None
|
||||
return kek_meta_dto
|
||||
|
||||
def create(self, bit_length, type_enum, algorithm=None, mode=None):
|
||||
byte_length = bit_length / 8
|
||||
return Random.get_random_bytes(byte_length)
|
||||
def generate(self, generate_dto, kek_meta_dto, keystone_id):
|
||||
byte_length = int(generate_dto.bit_length) / 8
|
||||
unencrypted = Random.get_random_bytes(byte_length)
|
||||
return self.encrypt(EncryptDTO(unencrypted), kek_meta_dto, keystone_id)
|
||||
|
||||
def supports(self, type_enum, algorithm=None, mode=None):
|
||||
if type_enum == PluginSupportTypes.ENCRYPT_DECRYPT:
|
||||
|
@ -25,16 +25,16 @@ from barbican.crypto.plugin import CryptoPluginBase, PluginSupportTypes
|
||||
class TestSupportsCryptoPlugin(CryptoPluginBase):
|
||||
"""Crypto plugin for testing supports."""
|
||||
|
||||
def encrypt(self, unencrypted, kek_meta_dto, tenant):
|
||||
def encrypt(self, encrypt_dto, kek_meta_dto, tenant):
|
||||
raise NotImplementedError()
|
||||
|
||||
def decrypt(self, encrypted, kek_meta_dto, kek_meta_extended, tenant):
|
||||
def decrypt(self, decrypt_dto, kek_meta_dto, kek_meta_extended, tenant):
|
||||
raise NotImplementedError()
|
||||
|
||||
def bind_kek_metadata(self, kek_meta_dto):
|
||||
return None
|
||||
|
||||
def create(self, bit_length, type_enum, algorithm=None, mode=None):
|
||||
def generate(self, generate_dto, type_enum, kek_meta_dto, keystone_id):
|
||||
raise NotImplementedError()
|
||||
|
||||
def supports(self, type_enum, algorithm=None, mode=None):
|
||||
|
@ -19,6 +19,7 @@ import testtools
|
||||
|
||||
from barbican.crypto import p11_crypto
|
||||
from barbican.crypto import plugin as plugin_import
|
||||
from barbican.model import models
|
||||
|
||||
|
||||
class WhenTestingP11CryptoPlugin(testtools.TestCase):
|
||||
@ -42,28 +43,41 @@ class WhenTestingP11CryptoPlugin(testtools.TestCase):
|
||||
super(WhenTestingP11CryptoPlugin, self).tearDown()
|
||||
self.patcher.stop()
|
||||
|
||||
def test_create_calls_generate_random(self):
|
||||
def test_generate_calls_generate_random(self):
|
||||
self.session.generateRandom.return_value = [1, 2, 3, 4, 5, 6, 7,
|
||||
8, 9, 10, 11, 12, 13,
|
||||
14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25,
|
||||
26, 27, 28, 29, 30, 31, 32]
|
||||
key = self.plugin.create(
|
||||
256,
|
||||
14, 15, 16]
|
||||
secret = models.Secret()
|
||||
secret.bit_length = 128
|
||||
secret.algorithm = "AES"
|
||||
generate_dto = plugin_import.GenerateDTO(
|
||||
plugin_import.PluginSupportTypes.SYMMETRIC_KEY_GENERATION,
|
||||
"AES"
|
||||
secret.algorithm,
|
||||
secret.bit_length,
|
||||
None)
|
||||
encrypted, kek_ext = self.plugin.generate(
|
||||
generate_dto,
|
||||
mock.MagicMock(),
|
||||
mock.MagicMock()
|
||||
)
|
||||
self.assertEqual(len(key), 32)
|
||||
self.session.generateRandom.assert_called_once_with(32)
|
||||
self.session.generateRandom.assert_called_twice_with(16)
|
||||
|
||||
def test_create_errors_when_rand_length_is_not_as_requested(self):
|
||||
def test_generate_errors_when_rand_length_is_not_as_requested(self):
|
||||
self.session.generateRandom.return_value = [1, 2, 3, 4, 5, 6, 7]
|
||||
secret = models.Secret()
|
||||
secret.bit_length = 192
|
||||
secret.algorithm = "AES"
|
||||
generate_dto = plugin_import.GenerateDTO(
|
||||
plugin_import.PluginSupportTypes.SYMMETRIC_KEY_GENERATION,
|
||||
secret.algorithm,
|
||||
secret.bit_length,
|
||||
None)
|
||||
self.assertRaises(
|
||||
p11_crypto.P11CryptoPluginException,
|
||||
self.plugin.create,
|
||||
192,
|
||||
plugin_import.PluginSupportTypes.SYMMETRIC_KEY_GENERATION,
|
||||
"AES",
|
||||
self.plugin.generate,
|
||||
generate_dto,
|
||||
mock.MagicMock(),
|
||||
mock.MagicMock()
|
||||
)
|
||||
|
||||
def test_raises_error_with_no_library_path(self):
|
||||
@ -153,7 +167,8 @@ class WhenTestingP11CryptoPlugin(testtools.TestCase):
|
||||
mech = mock.MagicMock()
|
||||
self.p11_mock.Mechanism.return_value = mech
|
||||
self.session.encrypt.return_value = [1, 2, 3, 4, 5]
|
||||
cyphertext, kek_meta_extended = self.plugin.encrypt(payload,
|
||||
encrypt_dto = plugin_import.EncryptDTO(payload)
|
||||
cyphertext, kek_meta_extended = self.plugin.encrypt(encrypt_dto,
|
||||
mock.MagicMock(),
|
||||
mock.MagicMock())
|
||||
|
||||
@ -172,7 +187,8 @@ class WhenTestingP11CryptoPlugin(testtools.TestCase):
|
||||
mech = mock.MagicMock()
|
||||
self.p11_mock.Mechanism.return_value = mech
|
||||
kek_meta_extended = '{"iv": "AQIDBAUGBwgJCgsMDQ4PEA=="}'
|
||||
payload = self.plugin.decrypt(ct,
|
||||
decrypt_dto = plugin_import.DecryptDTO(ct)
|
||||
payload = self.plugin.decrypt(decrypt_dto,
|
||||
mock.MagicMock(),
|
||||
kek_meta_extended,
|
||||
mock.MagicMock())
|
||||
|
@ -19,16 +19,18 @@ from mock import MagicMock
|
||||
import testtools
|
||||
|
||||
from barbican.crypto import plugin
|
||||
from barbican.model import models
|
||||
|
||||
|
||||
class TestCryptoPlugin(plugin.CryptoPluginBase):
|
||||
"""Crypto plugin implementation for testing the plugin manager."""
|
||||
|
||||
def encrypt(self, unencrypted, kek_meta_dto, keystone_id):
|
||||
def encrypt(self, encrypt_dto, kek_meta_dto, keystone_id):
|
||||
cypher_text = b'cypher_text'
|
||||
return cypher_text, None
|
||||
|
||||
def decrypt(self, encrypted, kek_meta_dto, kek_meta_extended, keystone_id):
|
||||
def decrypt(self, decrypt_dto, kek_meta_dto, kek_meta_extended,
|
||||
keystone_id):
|
||||
return b'unencrypted_data'
|
||||
|
||||
def bind_kek_metadata(self, kek_meta_dto):
|
||||
@ -38,8 +40,8 @@ class TestCryptoPlugin(plugin.CryptoPluginBase):
|
||||
kek_meta_dto.plugin_meta = None
|
||||
return kek_meta_dto
|
||||
|
||||
def create(self, bit_length, type_enum, algorithm=None, mode=None):
|
||||
return "insecure_key"
|
||||
def generate(self, generate_dto, kek_meta_dto, keystone_id):
|
||||
return "encrypted insecure key", None
|
||||
|
||||
def supports(self, type_enum, algorithm=None, mode=None):
|
||||
if type_enum == plugin.PluginSupportTypes.ENCRYPT_DECRYPT:
|
||||
@ -84,55 +86,93 @@ class WhenTestingSimpleCryptoPlugin(testtools.TestCase):
|
||||
|
||||
def test_encrypt_unicode_raises_value_error(self):
|
||||
unencrypted = u'unicode_beer\U0001F37A'
|
||||
encrypt_dto = plugin.EncryptDTO(unencrypted)
|
||||
secret = MagicMock()
|
||||
secret.mime_type = 'text/plain'
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
self.plugin.encrypt,
|
||||
unencrypted,
|
||||
encrypt_dto,
|
||||
MagicMock(),
|
||||
MagicMock(),
|
||||
)
|
||||
|
||||
def test_byte_string_encryption(self):
|
||||
unencrypted = b'some_secret'
|
||||
encrypted, kek_ext = self.plugin.encrypt(unencrypted,
|
||||
encrypt_dto = plugin.EncryptDTO(unencrypted)
|
||||
encrypted, kek_ext = self.plugin.encrypt(encrypt_dto,
|
||||
MagicMock(),
|
||||
MagicMock())
|
||||
decrypted = self.plugin.decrypt(encrypted, MagicMock(),
|
||||
decrypt_dto = plugin.DecryptDTO(encrypted)
|
||||
decrypted = self.plugin.decrypt(decrypt_dto, MagicMock(),
|
||||
kek_ext, MagicMock())
|
||||
self.assertEqual(unencrypted, decrypted)
|
||||
|
||||
def test_random_bytes_encryption(self):
|
||||
unencrypted = Random.get_random_bytes(10)
|
||||
encrypted, kek_meta_ext = self.plugin.encrypt(unencrypted,
|
||||
encrypt_dto = plugin.EncryptDTO(unencrypted)
|
||||
encrypted, kek_meta_ext = self.plugin.encrypt(encrypt_dto,
|
||||
MagicMock(), MagicMock())
|
||||
decrypted = self.plugin.decrypt(encrypted, MagicMock(),
|
||||
decrypt_dto = plugin.DecryptDTO(encrypted)
|
||||
decrypted = self.plugin.decrypt(decrypt_dto, MagicMock(),
|
||||
kek_meta_ext, MagicMock())
|
||||
self.assertEqual(unencrypted, decrypted)
|
||||
|
||||
def test_create_256_bit_key(self):
|
||||
key = self.plugin.create(
|
||||
256,
|
||||
def test_generate_256_bit_key(self):
|
||||
secret = models.Secret()
|
||||
secret.bit_length = 256
|
||||
secret.algorithm = "AES"
|
||||
generate_dto = plugin.GenerateDTO(
|
||||
plugin.PluginSupportTypes.SYMMETRIC_KEY_GENERATION,
|
||||
"AES"
|
||||
secret.algorithm,
|
||||
secret.bit_length,
|
||||
secret.mode)
|
||||
encrypted, kek_ext = self.plugin.generate(
|
||||
generate_dto,
|
||||
MagicMock(),
|
||||
MagicMock()
|
||||
)
|
||||
decrypt_dto = plugin.DecryptDTO(encrypted)
|
||||
key = self.plugin.decrypt(decrypt_dto, MagicMock(),
|
||||
kek_ext, MagicMock())
|
||||
self.assertEqual(len(key), 32)
|
||||
|
||||
def test_create_192_bit_key(self):
|
||||
key = self.plugin.create(
|
||||
192,
|
||||
def test_generate_192_bit_key(self):
|
||||
secret = models.Secret()
|
||||
secret.bit_length = 192
|
||||
secret.algorithm = "AES"
|
||||
generate_dto = plugin.GenerateDTO(
|
||||
plugin.PluginSupportTypes.SYMMETRIC_KEY_GENERATION,
|
||||
"AES"
|
||||
secret.algorithm,
|
||||
secret.bit_length,
|
||||
None)
|
||||
encrypted, kek_ext = self.plugin.generate(
|
||||
generate_dto,
|
||||
MagicMock(),
|
||||
MagicMock()
|
||||
)
|
||||
decrypt_dto = plugin.DecryptDTO(encrypted)
|
||||
key = self.plugin.decrypt(decrypt_dto, MagicMock(),
|
||||
kek_ext, MagicMock())
|
||||
self.assertEqual(len(key), 24)
|
||||
|
||||
def test_create_128_bit_key(self):
|
||||
key = self.plugin.create(
|
||||
128,
|
||||
def test_generate_128_bit_key(self):
|
||||
secret = models.Secret()
|
||||
secret.bit_length = 128
|
||||
secret.algorithm = "AES"
|
||||
generate_dto = plugin.GenerateDTO(
|
||||
plugin.PluginSupportTypes.SYMMETRIC_KEY_GENERATION,
|
||||
"AES"
|
||||
secret.algorithm,
|
||||
secret.bit_length,
|
||||
None)
|
||||
encrypted, kek_ext = self.plugin.generate(
|
||||
generate_dto,
|
||||
MagicMock(),
|
||||
MagicMock()
|
||||
)
|
||||
decrypt_dto = plugin.DecryptDTO(encrypted)
|
||||
key = self.plugin.decrypt(decrypt_dto, MagicMock(),
|
||||
kek_ext, MagicMock())
|
||||
self.assertEqual(len(key), 16)
|
||||
|
||||
def test_supports_encrypt_decrypt(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user