扫码登录,获取cookies
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/__init__.py: Self-test for cipher modules
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test for cipher modules"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
from Crypto.SelfTest.Cipher import test_AES; tests += test_AES.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_ARC2; tests += test_ARC2.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_ARC4; tests += test_ARC4.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_Blowfish; tests += test_Blowfish.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_CAST; tests += test_CAST.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_DES3; tests += test_DES3.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_DES; tests += test_DES.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_Salsa20; tests += test_Salsa20.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_ChaCha20; tests += test_ChaCha20.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_ChaCha20_Poly1305; tests += test_ChaCha20_Poly1305.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_pkcs1_15; tests += test_pkcs1_15.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_pkcs1_oaep; tests += test_pkcs1_oaep.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_OCB; tests += test_OCB.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_CBC; tests += test_CBC.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_CFB; tests += test_CFB.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_OpenPGP; tests += test_OpenPGP.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_OFB; tests += test_OFB.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_CTR; tests += test_CTR.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_CCM; tests += test_CCM.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_EAX; tests += test_EAX.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_GCM; tests += test_GCM.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_SIV; tests += test_SIV.get_tests(config=config)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
510
backend/venv/Lib/site-packages/Crypto/SelfTest/Cipher/common.py
Normal file
510
backend/venv/Lib/site-packages/Crypto/SelfTest/Cipher/common.py
Normal file
@@ -0,0 +1,510 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/common.py: Common code for Crypto.SelfTest.Hash
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-testing for PyCrypto hash modules"""
|
||||
|
||||
import unittest
|
||||
from binascii import a2b_hex, b2a_hex, hexlify
|
||||
|
||||
from Crypto.Util.py3compat import b
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
|
||||
class _NoDefault: pass # sentinel object
|
||||
def _extract(d, k, default=_NoDefault):
|
||||
"""Get an item from a dictionary, and remove it from the dictionary."""
|
||||
try:
|
||||
retval = d[k]
|
||||
except KeyError:
|
||||
if default is _NoDefault:
|
||||
raise
|
||||
return default
|
||||
del d[k]
|
||||
return retval
|
||||
|
||||
# Generic cipher test case
|
||||
class CipherSelfTest(unittest.TestCase):
|
||||
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
|
||||
# Extract the parameters
|
||||
params = params.copy()
|
||||
self.description = _extract(params, 'description')
|
||||
self.key = b(_extract(params, 'key'))
|
||||
self.plaintext = b(_extract(params, 'plaintext'))
|
||||
self.ciphertext = b(_extract(params, 'ciphertext'))
|
||||
self.module_name = _extract(params, 'module_name', None)
|
||||
self.assoc_data = _extract(params, 'assoc_data', None)
|
||||
self.mac = _extract(params, 'mac', None)
|
||||
if self.assoc_data:
|
||||
self.mac = b(self.mac)
|
||||
|
||||
mode = _extract(params, 'mode', None)
|
||||
self.mode_name = str(mode)
|
||||
|
||||
if mode is not None:
|
||||
# Block cipher
|
||||
self.mode = getattr(self.module, "MODE_" + mode)
|
||||
|
||||
self.iv = _extract(params, 'iv', None)
|
||||
if self.iv is None:
|
||||
self.iv = _extract(params, 'nonce', None)
|
||||
if self.iv is not None:
|
||||
self.iv = b(self.iv)
|
||||
|
||||
else:
|
||||
# Stream cipher
|
||||
self.mode = None
|
||||
self.iv = _extract(params, 'iv', None)
|
||||
if self.iv is not None:
|
||||
self.iv = b(self.iv)
|
||||
|
||||
self.extra_params = params
|
||||
|
||||
def shortDescription(self):
|
||||
return self.description
|
||||
|
||||
def _new(self):
|
||||
params = self.extra_params.copy()
|
||||
key = a2b_hex(self.key)
|
||||
|
||||
old_style = []
|
||||
if self.mode is not None:
|
||||
old_style = [ self.mode ]
|
||||
if self.iv is not None:
|
||||
old_style += [ a2b_hex(self.iv) ]
|
||||
|
||||
return self.module.new(key, *old_style, **params)
|
||||
|
||||
def isMode(self, name):
|
||||
if not hasattr(self.module, "MODE_"+name):
|
||||
return False
|
||||
return self.mode == getattr(self.module, "MODE_"+name)
|
||||
|
||||
def runTest(self):
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
assoc_data = []
|
||||
if self.assoc_data:
|
||||
assoc_data = [ a2b_hex(b(x)) for x in self.assoc_data]
|
||||
|
||||
ct = None
|
||||
pt = None
|
||||
|
||||
#
|
||||
# Repeat the same encryption or decryption twice and verify
|
||||
# that the result is always the same
|
||||
#
|
||||
for i in range(2):
|
||||
cipher = self._new()
|
||||
decipher = self._new()
|
||||
|
||||
# Only AEAD modes
|
||||
for comp in assoc_data:
|
||||
cipher.update(comp)
|
||||
decipher.update(comp)
|
||||
|
||||
ctX = b2a_hex(cipher.encrypt(plaintext))
|
||||
ptX = b2a_hex(decipher.decrypt(ciphertext))
|
||||
|
||||
if ct:
|
||||
self.assertEqual(ct, ctX)
|
||||
self.assertEqual(pt, ptX)
|
||||
ct, pt = ctX, ptX
|
||||
|
||||
self.assertEqual(self.ciphertext, ct) # encrypt
|
||||
self.assertEqual(self.plaintext, pt) # decrypt
|
||||
|
||||
if self.mac:
|
||||
mac = b2a_hex(cipher.digest())
|
||||
self.assertEqual(self.mac, mac)
|
||||
decipher.verify(a2b_hex(self.mac))
|
||||
|
||||
class CipherStreamingSelfTest(CipherSelfTest):
|
||||
|
||||
def shortDescription(self):
|
||||
desc = self.module_name
|
||||
if self.mode is not None:
|
||||
desc += " in %s mode" % (self.mode_name,)
|
||||
return "%s should behave like a stream cipher" % (desc,)
|
||||
|
||||
def runTest(self):
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
|
||||
# The cipher should work like a stream cipher
|
||||
|
||||
# Test counter mode encryption, 3 bytes at a time
|
||||
ct3 = []
|
||||
cipher = self._new()
|
||||
for i in range(0, len(plaintext), 3):
|
||||
ct3.append(cipher.encrypt(plaintext[i:i+3]))
|
||||
ct3 = b2a_hex(b("").join(ct3))
|
||||
self.assertEqual(self.ciphertext, ct3) # encryption (3 bytes at a time)
|
||||
|
||||
# Test counter mode decryption, 3 bytes at a time
|
||||
pt3 = []
|
||||
cipher = self._new()
|
||||
for i in range(0, len(ciphertext), 3):
|
||||
pt3.append(cipher.encrypt(ciphertext[i:i+3]))
|
||||
# PY3K: This is meant to be text, do not change to bytes (data)
|
||||
pt3 = b2a_hex(b("").join(pt3))
|
||||
self.assertEqual(self.plaintext, pt3) # decryption (3 bytes at a time)
|
||||
|
||||
|
||||
class RoundtripTest(unittest.TestCase):
|
||||
def __init__(self, module, params):
|
||||
from Crypto import Random
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.iv = Random.get_random_bytes(module.block_size)
|
||||
self.key = b(params['key'])
|
||||
self.plaintext = 100 * b(params['plaintext'])
|
||||
self.module_name = params.get('module_name', None)
|
||||
|
||||
def shortDescription(self):
|
||||
return """%s .decrypt() output of .encrypt() should not be garbled""" % (self.module_name,)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
## ECB mode
|
||||
mode = self.module.MODE_ECB
|
||||
encryption_cipher = self.module.new(a2b_hex(self.key), mode)
|
||||
ciphertext = encryption_cipher.encrypt(self.plaintext)
|
||||
decryption_cipher = self.module.new(a2b_hex(self.key), mode)
|
||||
decrypted_plaintext = decryption_cipher.decrypt(ciphertext)
|
||||
self.assertEqual(self.plaintext, decrypted_plaintext)
|
||||
|
||||
|
||||
class IVLengthTest(unittest.TestCase):
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.key = b(params['key'])
|
||||
|
||||
def shortDescription(self):
|
||||
return "Check that all modes except MODE_ECB and MODE_CTR require an IV of the proper length"
|
||||
|
||||
def runTest(self):
|
||||
self.assertRaises(TypeError, self.module.new, a2b_hex(self.key),
|
||||
self.module.MODE_ECB, b(""))
|
||||
|
||||
def _dummy_counter(self):
|
||||
return "\0" * self.module.block_size
|
||||
|
||||
|
||||
class NoDefaultECBTest(unittest.TestCase):
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.key = b(params['key'])
|
||||
|
||||
def runTest(self):
|
||||
self.assertRaises(TypeError, self.module.new, a2b_hex(self.key))
|
||||
|
||||
|
||||
class BlockSizeTest(unittest.TestCase):
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.key = a2b_hex(b(params['key']))
|
||||
|
||||
def runTest(self):
|
||||
cipher = self.module.new(self.key, self.module.MODE_ECB)
|
||||
self.assertEqual(cipher.block_size, self.module.block_size)
|
||||
|
||||
|
||||
class ByteArrayTest(unittest.TestCase):
|
||||
"""Verify we can use bytearray's for encrypting and decrypting"""
|
||||
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
|
||||
# Extract the parameters
|
||||
params = params.copy()
|
||||
self.description = _extract(params, 'description')
|
||||
self.key = b(_extract(params, 'key'))
|
||||
self.plaintext = b(_extract(params, 'plaintext'))
|
||||
self.ciphertext = b(_extract(params, 'ciphertext'))
|
||||
self.module_name = _extract(params, 'module_name', None)
|
||||
self.assoc_data = _extract(params, 'assoc_data', None)
|
||||
self.mac = _extract(params, 'mac', None)
|
||||
if self.assoc_data:
|
||||
self.mac = b(self.mac)
|
||||
|
||||
mode = _extract(params, 'mode', None)
|
||||
self.mode_name = str(mode)
|
||||
|
||||
if mode is not None:
|
||||
# Block cipher
|
||||
self.mode = getattr(self.module, "MODE_" + mode)
|
||||
|
||||
self.iv = _extract(params, 'iv', None)
|
||||
if self.iv is None:
|
||||
self.iv = _extract(params, 'nonce', None)
|
||||
if self.iv is not None:
|
||||
self.iv = b(self.iv)
|
||||
else:
|
||||
# Stream cipher
|
||||
self.mode = None
|
||||
self.iv = _extract(params, 'iv', None)
|
||||
if self.iv is not None:
|
||||
self.iv = b(self.iv)
|
||||
|
||||
self.extra_params = params
|
||||
|
||||
def _new(self):
|
||||
params = self.extra_params.copy()
|
||||
key = a2b_hex(self.key)
|
||||
|
||||
old_style = []
|
||||
if self.mode is not None:
|
||||
old_style = [ self.mode ]
|
||||
if self.iv is not None:
|
||||
old_style += [ a2b_hex(self.iv) ]
|
||||
|
||||
return self.module.new(key, *old_style, **params)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
assoc_data = []
|
||||
if self.assoc_data:
|
||||
assoc_data = [ bytearray(a2b_hex(b(x))) for x in self.assoc_data]
|
||||
|
||||
cipher = self._new()
|
||||
decipher = self._new()
|
||||
|
||||
# Only AEAD modes
|
||||
for comp in assoc_data:
|
||||
cipher.update(comp)
|
||||
decipher.update(comp)
|
||||
|
||||
ct = b2a_hex(cipher.encrypt(bytearray(plaintext)))
|
||||
pt = b2a_hex(decipher.decrypt(bytearray(ciphertext)))
|
||||
|
||||
self.assertEqual(self.ciphertext, ct) # encrypt
|
||||
self.assertEqual(self.plaintext, pt) # decrypt
|
||||
|
||||
if self.mac:
|
||||
mac = b2a_hex(cipher.digest())
|
||||
self.assertEqual(self.mac, mac)
|
||||
decipher.verify(bytearray(a2b_hex(self.mac)))
|
||||
|
||||
|
||||
class MemoryviewTest(unittest.TestCase):
|
||||
"""Verify we can use memoryviews for encrypting and decrypting"""
|
||||
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
|
||||
# Extract the parameters
|
||||
params = params.copy()
|
||||
self.description = _extract(params, 'description')
|
||||
self.key = b(_extract(params, 'key'))
|
||||
self.plaintext = b(_extract(params, 'plaintext'))
|
||||
self.ciphertext = b(_extract(params, 'ciphertext'))
|
||||
self.module_name = _extract(params, 'module_name', None)
|
||||
self.assoc_data = _extract(params, 'assoc_data', None)
|
||||
self.mac = _extract(params, 'mac', None)
|
||||
if self.assoc_data:
|
||||
self.mac = b(self.mac)
|
||||
|
||||
mode = _extract(params, 'mode', None)
|
||||
self.mode_name = str(mode)
|
||||
|
||||
if mode is not None:
|
||||
# Block cipher
|
||||
self.mode = getattr(self.module, "MODE_" + mode)
|
||||
|
||||
self.iv = _extract(params, 'iv', None)
|
||||
if self.iv is None:
|
||||
self.iv = _extract(params, 'nonce', None)
|
||||
if self.iv is not None:
|
||||
self.iv = b(self.iv)
|
||||
else:
|
||||
# Stream cipher
|
||||
self.mode = None
|
||||
self.iv = _extract(params, 'iv', None)
|
||||
if self.iv is not None:
|
||||
self.iv = b(self.iv)
|
||||
|
||||
self.extra_params = params
|
||||
|
||||
def _new(self):
|
||||
params = self.extra_params.copy()
|
||||
key = a2b_hex(self.key)
|
||||
|
||||
old_style = []
|
||||
if self.mode is not None:
|
||||
old_style = [ self.mode ]
|
||||
if self.iv is not None:
|
||||
old_style += [ a2b_hex(self.iv) ]
|
||||
|
||||
return self.module.new(key, *old_style, **params)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
assoc_data = []
|
||||
if self.assoc_data:
|
||||
assoc_data = [ memoryview(a2b_hex(b(x))) for x in self.assoc_data]
|
||||
|
||||
cipher = self._new()
|
||||
decipher = self._new()
|
||||
|
||||
# Only AEAD modes
|
||||
for comp in assoc_data:
|
||||
cipher.update(comp)
|
||||
decipher.update(comp)
|
||||
|
||||
ct = b2a_hex(cipher.encrypt(memoryview(plaintext)))
|
||||
pt = b2a_hex(decipher.decrypt(memoryview(ciphertext)))
|
||||
|
||||
self.assertEqual(self.ciphertext, ct) # encrypt
|
||||
self.assertEqual(self.plaintext, pt) # decrypt
|
||||
|
||||
if self.mac:
|
||||
mac = b2a_hex(cipher.digest())
|
||||
self.assertEqual(self.mac, mac)
|
||||
decipher.verify(memoryview(a2b_hex(self.mac)))
|
||||
|
||||
|
||||
def make_block_tests(module, module_name, test_data, additional_params=dict()):
|
||||
tests = []
|
||||
extra_tests_added = False
|
||||
for i in range(len(test_data)):
|
||||
row = test_data[i]
|
||||
|
||||
# Build the "params" dictionary with
|
||||
# - plaintext
|
||||
# - ciphertext
|
||||
# - key
|
||||
# - mode (default is ECB)
|
||||
# - (optionally) description
|
||||
# - (optionally) any other parameter that this cipher mode requires
|
||||
params = {}
|
||||
if len(row) == 3:
|
||||
(params['plaintext'], params['ciphertext'], params['key']) = row
|
||||
elif len(row) == 4:
|
||||
(params['plaintext'], params['ciphertext'], params['key'], params['description']) = row
|
||||
elif len(row) == 5:
|
||||
(params['plaintext'], params['ciphertext'], params['key'], params['description'], extra_params) = row
|
||||
params.update(extra_params)
|
||||
else:
|
||||
raise AssertionError("Unsupported tuple size %d" % (len(row),))
|
||||
|
||||
if not "mode" in params:
|
||||
params["mode"] = "ECB"
|
||||
|
||||
# Build the display-name for the test
|
||||
p2 = params.copy()
|
||||
p_key = _extract(p2, 'key')
|
||||
p_plaintext = _extract(p2, 'plaintext')
|
||||
p_ciphertext = _extract(p2, 'ciphertext')
|
||||
p_mode = _extract(p2, 'mode')
|
||||
p_description = _extract(p2, 'description', None)
|
||||
|
||||
if p_description is not None:
|
||||
description = p_description
|
||||
elif p_mode == 'ECB' and not p2:
|
||||
description = "p=%s, k=%s" % (p_plaintext, p_key)
|
||||
else:
|
||||
description = "p=%s, k=%s, %r" % (p_plaintext, p_key, p2)
|
||||
name = "%s #%d: %s" % (module_name, i+1, description)
|
||||
params['description'] = name
|
||||
params['module_name'] = module_name
|
||||
params.update(additional_params)
|
||||
|
||||
# Add extra test(s) to the test suite before the current test
|
||||
if not extra_tests_added:
|
||||
tests += [
|
||||
RoundtripTest(module, params),
|
||||
IVLengthTest(module, params),
|
||||
NoDefaultECBTest(module, params),
|
||||
ByteArrayTest(module, params),
|
||||
BlockSizeTest(module, params),
|
||||
]
|
||||
extra_tests_added = True
|
||||
|
||||
# Add the current test to the test suite
|
||||
tests.append(CipherSelfTest(module, params))
|
||||
|
||||
return tests
|
||||
|
||||
def make_stream_tests(module, module_name, test_data):
|
||||
tests = []
|
||||
extra_tests_added = False
|
||||
for i in range(len(test_data)):
|
||||
row = test_data[i]
|
||||
|
||||
# Build the "params" dictionary
|
||||
params = {}
|
||||
if len(row) == 3:
|
||||
(params['plaintext'], params['ciphertext'], params['key']) = row
|
||||
elif len(row) == 4:
|
||||
(params['plaintext'], params['ciphertext'], params['key'], params['description']) = row
|
||||
elif len(row) == 5:
|
||||
(params['plaintext'], params['ciphertext'], params['key'], params['description'], extra_params) = row
|
||||
params.update(extra_params)
|
||||
else:
|
||||
raise AssertionError("Unsupported tuple size %d" % (len(row),))
|
||||
|
||||
# Build the display-name for the test
|
||||
p2 = params.copy()
|
||||
p_key = _extract(p2, 'key')
|
||||
p_plaintext = _extract(p2, 'plaintext')
|
||||
p_ciphertext = _extract(p2, 'ciphertext')
|
||||
p_description = _extract(p2, 'description', None)
|
||||
|
||||
if p_description is not None:
|
||||
description = p_description
|
||||
elif not p2:
|
||||
description = "p=%s, k=%s" % (p_plaintext, p_key)
|
||||
else:
|
||||
description = "p=%s, k=%s, %r" % (p_plaintext, p_key, p2)
|
||||
name = "%s #%d: %s" % (module_name, i+1, description)
|
||||
params['description'] = name
|
||||
params['module_name'] = module_name
|
||||
|
||||
# Add extra test(s) to the test suite before the current test
|
||||
if not extra_tests_added:
|
||||
tests += [
|
||||
ByteArrayTest(module, params),
|
||||
]
|
||||
|
||||
tests.append(MemoryviewTest(module, params))
|
||||
extra_tests_added = True
|
||||
|
||||
# Add the test to the test suite
|
||||
tests.append(CipherSelfTest(module, params))
|
||||
tests.append(CipherStreamingSelfTest(module, params))
|
||||
return tests
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
1351
backend/venv/Lib/site-packages/Crypto/SelfTest/Cipher/test_AES.py
Normal file
1351
backend/venv/Lib/site-packages/Crypto/SelfTest/Cipher/test_AES.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,167 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/ARC2.py: Self-test for the Alleged-RC2 cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.ARC2"""
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.Util.py3compat import b, bchr
|
||||
|
||||
from Crypto.Cipher import ARC2
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key[, description[, extra_params]]) tuples.
|
||||
test_data = [
|
||||
# Test vectors from RFC 2268
|
||||
|
||||
# 63-bit effective key length
|
||||
('0000000000000000', 'ebb773f993278eff', '0000000000000000',
|
||||
'RFC2268-1', dict(effective_keylen=63)),
|
||||
|
||||
# 64-bit effective key length
|
||||
('ffffffffffffffff', '278b27e42e2f0d49', 'ffffffffffffffff',
|
||||
'RFC2268-2', dict(effective_keylen=64)),
|
||||
('1000000000000001', '30649edf9be7d2c2', '3000000000000000',
|
||||
'RFC2268-3', dict(effective_keylen=64)),
|
||||
#('0000000000000000', '61a8a244adacccf0', '88',
|
||||
# 'RFC2268-4', dict(effective_keylen=64)),
|
||||
('0000000000000000', '6ccf4308974c267f', '88bca90e90875a',
|
||||
'RFC2268-5', dict(effective_keylen=64)),
|
||||
('0000000000000000', '1a807d272bbe5db1', '88bca90e90875a7f0f79c384627bafb2',
|
||||
'RFC2268-6', dict(effective_keylen=64)),
|
||||
|
||||
# 128-bit effective key length
|
||||
('0000000000000000', '2269552ab0f85ca6', '88bca90e90875a7f0f79c384627bafb2',
|
||||
"RFC2268-7", dict(effective_keylen=128)),
|
||||
('0000000000000000', '5b78d3a43dfff1f1',
|
||||
'88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e',
|
||||
"RFC2268-8", dict(effective_keylen=129)),
|
||||
|
||||
# Test vectors from PyCrypto 2.0.1's testdata.py
|
||||
# 1024-bit effective key length
|
||||
('0000000000000000', '624fb3e887419e48', '5068696c6970476c617373',
|
||||
'PCTv201-0'),
|
||||
('ffffffffffffffff', '79cadef44c4a5a85', '5068696c6970476c617373',
|
||||
'PCTv201-1'),
|
||||
('0001020304050607', '90411525b34e4c2c', '5068696c6970476c617373',
|
||||
'PCTv201-2'),
|
||||
('0011223344556677', '078656aaba61cbfb', '5068696c6970476c617373',
|
||||
'PCTv201-3'),
|
||||
('0000000000000000', 'd7bcc5dbb4d6e56a', 'ffffffffffffffff',
|
||||
'PCTv201-4'),
|
||||
('ffffffffffffffff', '7259018ec557b357', 'ffffffffffffffff',
|
||||
'PCTv201-5'),
|
||||
('0001020304050607', '93d20a497f2ccb62', 'ffffffffffffffff',
|
||||
'PCTv201-6'),
|
||||
('0011223344556677', 'cb15a7f819c0014d', 'ffffffffffffffff',
|
||||
'PCTv201-7'),
|
||||
('0000000000000000', '63ac98cdf3843a7a', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553',
|
||||
'PCTv201-8'),
|
||||
('ffffffffffffffff', '3fb49e2fa12371dd', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553',
|
||||
'PCTv201-9'),
|
||||
('0001020304050607', '46414781ab387d5f', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553',
|
||||
'PCTv201-10'),
|
||||
('0011223344556677', 'be09dc81feaca271', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553',
|
||||
'PCTv201-11'),
|
||||
('0000000000000000', 'e64221e608be30ab', '53e5ffe553',
|
||||
'PCTv201-12'),
|
||||
('ffffffffffffffff', '862bc60fdcd4d9a9', '53e5ffe553',
|
||||
'PCTv201-13'),
|
||||
('0001020304050607', '6a34da50fa5e47de', '53e5ffe553',
|
||||
'PCTv201-14'),
|
||||
('0011223344556677', '584644c34503122c', '53e5ffe553',
|
||||
'PCTv201-15'),
|
||||
]
|
||||
|
||||
class BufferOverflowTest(unittest.TestCase):
|
||||
# Test a buffer overflow found in older versions of PyCrypto
|
||||
|
||||
def runTest(self):
|
||||
"""ARC2 with keylength > 128"""
|
||||
key = b("x") * 16384
|
||||
self.assertRaises(ValueError, ARC2.new, key, ARC2.MODE_ECB)
|
||||
|
||||
class KeyLength(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
ARC2.new(b'\x00' * 16, ARC2.MODE_ECB, effective_keylen=40)
|
||||
self.assertRaises(ValueError, ARC2.new, bchr(0) * 4, ARC2.MODE_ECB)
|
||||
self.assertRaises(ValueError, ARC2.new, bchr(0) * 129, ARC2.MODE_ECB)
|
||||
|
||||
self.assertRaises(ValueError, ARC2.new, bchr(0) * 16, ARC2.MODE_ECB,
|
||||
effective_keylen=39)
|
||||
self.assertRaises(ValueError, ARC2.new, bchr(0) * 16, ARC2.MODE_ECB,
|
||||
effective_keylen=1025)
|
||||
|
||||
|
||||
class TestOutput(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
# Encrypt/Decrypt data and test output parameter
|
||||
|
||||
cipher = ARC2.new(b'4'*16, ARC2.MODE_ECB)
|
||||
|
||||
pt = b'5' * 16
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = bytearray(16)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
output = memoryview(bytearray(16))
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16)
|
||||
|
||||
shorter_output = bytearray(7)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Cipher import ARC2
|
||||
from .common import make_block_tests
|
||||
|
||||
tests = make_block_tests(ARC2, "ARC2", test_data)
|
||||
tests.append(BufferOverflowTest())
|
||||
tests.append(KeyLength())
|
||||
tests += [TestOutput()]
|
||||
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
@@ -0,0 +1,471 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/ARC4.py: Self-test for the Alleged-RC4 cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.ARC4"""
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.Util.py3compat import b
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.Cipher import ARC4
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key[, description]) tuples.
|
||||
test_data = [
|
||||
# Test vectors from Eric Rescorla's message with the subject
|
||||
# "RC4 compatibility testing", sent to the cipherpunks mailing list on
|
||||
# September 13, 1994.
|
||||
# http://cypherpunks.venona.com/date/1994/09/msg00420.html
|
||||
|
||||
('0123456789abcdef', '75b7878099e0c596', '0123456789abcdef',
|
||||
'Test vector 0'),
|
||||
|
||||
('0000000000000000', '7494c2e7104b0879', '0123456789abcdef',
|
||||
'Test vector 1'),
|
||||
|
||||
('0000000000000000', 'de188941a3375d3a', '0000000000000000',
|
||||
'Test vector 2'),
|
||||
|
||||
('00000000000000000000', 'd6a141a7ec3c38dfbd61', 'ef012345',
|
||||
'Test vector 3'),
|
||||
|
||||
('01' * 512,
|
||||
'7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76533449b6778dcad8'
|
||||
+ 'c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b1b13b6b919b8'
|
||||
+ '47c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd377108f98fd'
|
||||
+ 'cbd4e726567500990bcc7e0ca3c4aaa304a387d20f3b8fbbcd42a1bd311d7a43'
|
||||
+ '03dda5ab078896ae80c18b0af66dff319616eb784e495ad2ce90d7f772a81747'
|
||||
+ 'b65f62093b1e0db9e5ba532fafec47508323e671327df9444432cb7367cec82f'
|
||||
+ '5d44c0d00b67d650a075cd4b70dedd77eb9b10231b6b5b741347396d62897421'
|
||||
+ 'd43df9b42e446e358e9c11a9b2184ecbef0cd8e7a877ef968f1390ec9b3d35a5'
|
||||
+ '585cb009290e2fcde7b5ec66d9084be44055a619d9dd7fc3166f9487f7cb2729'
|
||||
+ '12426445998514c15d53a18c864ce3a2b7555793988126520eacf2e3066e230c'
|
||||
+ '91bee4dd5304f5fd0405b35bd99c73135d3d9bc335ee049ef69b3867bf2d7bd1'
|
||||
+ 'eaa595d8bfc0066ff8d31509eb0c6caa006c807a623ef84c3d33c195d23ee320'
|
||||
+ 'c40de0558157c822d4b8c569d849aed59d4e0fd7f379586b4b7ff684ed6a189f'
|
||||
+ '7486d49b9c4bad9ba24b96abf924372c8a8fffb10d55354900a77a3db5f205e1'
|
||||
+ 'b99fcd8660863a159ad4abe40fa48934163ddde542a6585540fd683cbfd8c00f'
|
||||
+ '12129a284deacc4cdefe58be7137541c047126c8d49e2755ab181ab7e940b0c0',
|
||||
'0123456789abcdef',
|
||||
"Test vector 4"),
|
||||
# shortest key - generated with arc4 package
|
||||
('7468697320697320616e206578616d706c65',
|
||||
'7260677d38495a09585d69321e17eaf3cdd0',
|
||||
'01'),
|
||||
]
|
||||
|
||||
|
||||
class RFC6229_Tests(unittest.TestCase):
|
||||
# Test vectors from RFC 6229. Each test vector is a tuple with two items:
|
||||
# the ARC4 key and a dictionary. The dictionary has keystream offsets as keys
|
||||
# and the 16-byte keystream starting at the relevant offset as value.
|
||||
rfc6229_data = [
|
||||
# Page 3
|
||||
(
|
||||
'0102030405',
|
||||
{
|
||||
0: 'b2 39 63 05 f0 3d c0 27 cc c3 52 4a 0a 11 18 a8',
|
||||
16: '69 82 94 4f 18 fc 82 d5 89 c4 03 a4 7a 0d 09 19',
|
||||
240: '28 cb 11 32 c9 6c e2 86 42 1d ca ad b8 b6 9e ae',
|
||||
256: '1c fc f6 2b 03 ed db 64 1d 77 df cf 7f 8d 8c 93',
|
||||
496: '42 b7 d0 cd d9 18 a8 a3 3d d5 17 81 c8 1f 40 41',
|
||||
512: '64 59 84 44 32 a7 da 92 3c fb 3e b4 98 06 61 f6',
|
||||
752: 'ec 10 32 7b de 2b ee fd 18 f9 27 76 80 45 7e 22',
|
||||
768: 'eb 62 63 8d 4f 0b a1 fe 9f ca 20 e0 5b f8 ff 2b',
|
||||
1008: '45 12 90 48 e6 a0 ed 0b 56 b4 90 33 8f 07 8d a5',
|
||||
1024: '30 ab bc c7 c2 0b 01 60 9f 23 ee 2d 5f 6b b7 df',
|
||||
1520: '32 94 f7 44 d8 f9 79 05 07 e7 0f 62 e5 bb ce ea',
|
||||
1536: 'd8 72 9d b4 18 82 25 9b ee 4f 82 53 25 f5 a1 30',
|
||||
2032: '1e b1 4a 0c 13 b3 bf 47 fa 2a 0b a9 3a d4 5b 8b',
|
||||
2048: 'cc 58 2f 8b a9 f2 65 e2 b1 be 91 12 e9 75 d2 d7',
|
||||
3056: 'f2 e3 0f 9b d1 02 ec bf 75 aa ad e9 bc 35 c4 3c',
|
||||
3072: 'ec 0e 11 c4 79 dc 32 9d c8 da 79 68 fe 96 56 81',
|
||||
4080: '06 83 26 a2 11 84 16 d2 1f 9d 04 b2 cd 1c a0 50',
|
||||
4096: 'ff 25 b5 89 95 99 67 07 e5 1f bd f0 8b 34 d8 75'
|
||||
}
|
||||
),
|
||||
# Page 4
|
||||
(
|
||||
'01020304050607',
|
||||
{
|
||||
0: '29 3f 02 d4 7f 37 c9 b6 33 f2 af 52 85 fe b4 6b',
|
||||
16: 'e6 20 f1 39 0d 19 bd 84 e2 e0 fd 75 20 31 af c1',
|
||||
240: '91 4f 02 53 1c 92 18 81 0d f6 0f 67 e3 38 15 4c',
|
||||
256: 'd0 fd b5 83 07 3c e8 5a b8 39 17 74 0e c0 11 d5',
|
||||
496: '75 f8 14 11 e8 71 cf fa 70 b9 0c 74 c5 92 e4 54',
|
||||
512: '0b b8 72 02 93 8d ad 60 9e 87 a5 a1 b0 79 e5 e4',
|
||||
752: 'c2 91 12 46 b6 12 e7 e7 b9 03 df ed a1 da d8 66',
|
||||
768: '32 82 8f 91 50 2b 62 91 36 8d e8 08 1d e3 6f c2',
|
||||
1008: 'f3 b9 a7 e3 b2 97 bf 9a d8 04 51 2f 90 63 ef f1',
|
||||
1024: '8e cb 67 a9 ba 1f 55 a5 a0 67 e2 b0 26 a3 67 6f',
|
||||
1520: 'd2 aa 90 2b d4 2d 0d 7c fd 34 0c d4 58 10 52 9f',
|
||||
1536: '78 b2 72 c9 6e 42 ea b4 c6 0b d9 14 e3 9d 06 e3',
|
||||
2032: 'f4 33 2f d3 1a 07 93 96 ee 3c ee 3f 2a 4f f0 49',
|
||||
2048: '05 45 97 81 d4 1f da 7f 30 c1 be 7e 12 46 c6 23',
|
||||
3056: 'ad fd 38 68 b8 e5 14 85 d5 e6 10 01 7e 3d d6 09',
|
||||
3072: 'ad 26 58 1c 0c 5b e4 5f 4c ea 01 db 2f 38 05 d5',
|
||||
4080: 'f3 17 2c ef fc 3b 3d 99 7c 85 cc d5 af 1a 95 0c',
|
||||
4096: 'e7 4b 0b 97 31 22 7f d3 7c 0e c0 8a 47 dd d8 b8'
|
||||
}
|
||||
),
|
||||
(
|
||||
'0102030405060708',
|
||||
{
|
||||
0: '97 ab 8a 1b f0 af b9 61 32 f2 f6 72 58 da 15 a8',
|
||||
16: '82 63 ef db 45 c4 a1 86 84 ef 87 e6 b1 9e 5b 09',
|
||||
240: '96 36 eb c9 84 19 26 f4 f7 d1 f3 62 bd df 6e 18',
|
||||
256: 'd0 a9 90 ff 2c 05 fe f5 b9 03 73 c9 ff 4b 87 0a',
|
||||
496: '73 23 9f 1d b7 f4 1d 80 b6 43 c0 c5 25 18 ec 63',
|
||||
512: '16 3b 31 99 23 a6 bd b4 52 7c 62 61 26 70 3c 0f',
|
||||
752: '49 d6 c8 af 0f 97 14 4a 87 df 21 d9 14 72 f9 66',
|
||||
768: '44 17 3a 10 3b 66 16 c5 d5 ad 1c ee 40 c8 63 d0',
|
||||
1008: '27 3c 9c 4b 27 f3 22 e4 e7 16 ef 53 a4 7d e7 a4',
|
||||
1024: 'c6 d0 e7 b2 26 25 9f a9 02 34 90 b2 61 67 ad 1d',
|
||||
1520: '1f e8 98 67 13 f0 7c 3d 9a e1 c1 63 ff 8c f9 d3',
|
||||
1536: '83 69 e1 a9 65 61 0b e8 87 fb d0 c7 91 62 aa fb',
|
||||
2032: '0a 01 27 ab b4 44 84 b9 fb ef 5a bc ae 1b 57 9f',
|
||||
2048: 'c2 cd ad c6 40 2e 8e e8 66 e1 f3 7b db 47 e4 2c',
|
||||
3056: '26 b5 1e a3 7d f8 e1 d6 f7 6f c3 b6 6a 74 29 b3',
|
||||
3072: 'bc 76 83 20 5d 4f 44 3d c1 f2 9d da 33 15 c8 7b',
|
||||
4080: 'd5 fa 5a 34 69 d2 9a aa f8 3d 23 58 9d b8 c8 5b',
|
||||
4096: '3f b4 6e 2c 8f 0f 06 8e dc e8 cd cd 7d fc 58 62'
|
||||
}
|
||||
),
|
||||
# Page 5
|
||||
(
|
||||
'0102030405060708090a',
|
||||
{
|
||||
0: 'ed e3 b0 46 43 e5 86 cc 90 7d c2 18 51 70 99 02',
|
||||
16: '03 51 6b a7 8f 41 3b eb 22 3a a5 d4 d2 df 67 11',
|
||||
240: '3c fd 6c b5 8e e0 fd de 64 01 76 ad 00 00 04 4d',
|
||||
256: '48 53 2b 21 fb 60 79 c9 11 4c 0f fd 9c 04 a1 ad',
|
||||
496: '3e 8c ea 98 01 71 09 97 90 84 b1 ef 92 f9 9d 86',
|
||||
512: 'e2 0f b4 9b db 33 7e e4 8b 8d 8d c0 f4 af ef fe',
|
||||
752: '5c 25 21 ea cd 79 66 f1 5e 05 65 44 be a0 d3 15',
|
||||
768: 'e0 67 a7 03 19 31 a2 46 a6 c3 87 5d 2f 67 8a cb',
|
||||
1008: 'a6 4f 70 af 88 ae 56 b6 f8 75 81 c0 e2 3e 6b 08',
|
||||
1024: 'f4 49 03 1d e3 12 81 4e c6 f3 19 29 1f 4a 05 16',
|
||||
1520: 'bd ae 85 92 4b 3c b1 d0 a2 e3 3a 30 c6 d7 95 99',
|
||||
1536: '8a 0f ed db ac 86 5a 09 bc d1 27 fb 56 2e d6 0a',
|
||||
2032: 'b5 5a 0a 5b 51 a1 2a 8b e3 48 99 c3 e0 47 51 1a',
|
||||
2048: 'd9 a0 9c ea 3c e7 5f e3 96 98 07 03 17 a7 13 39',
|
||||
3056: '55 22 25 ed 11 77 f4 45 84 ac 8c fa 6c 4e b5 fc',
|
||||
3072: '7e 82 cb ab fc 95 38 1b 08 09 98 44 21 29 c2 f8',
|
||||
4080: '1f 13 5e d1 4c e6 0a 91 36 9d 23 22 be f2 5e 3c',
|
||||
4096: '08 b6 be 45 12 4a 43 e2 eb 77 95 3f 84 dc 85 53'
|
||||
}
|
||||
),
|
||||
(
|
||||
'0102030405060708090a0b0c0d0e0f10',
|
||||
{
|
||||
0: '9a c7 cc 9a 60 9d 1e f7 b2 93 28 99 cd e4 1b 97',
|
||||
16: '52 48 c4 95 90 14 12 6a 6e 8a 84 f1 1d 1a 9e 1c',
|
||||
240: '06 59 02 e4 b6 20 f6 cc 36 c8 58 9f 66 43 2f 2b',
|
||||
256: 'd3 9d 56 6b c6 bc e3 01 07 68 15 15 49 f3 87 3f',
|
||||
496: 'b6 d1 e6 c4 a5 e4 77 1c ad 79 53 8d f2 95 fb 11',
|
||||
512: 'c6 8c 1d 5c 55 9a 97 41 23 df 1d bc 52 a4 3b 89',
|
||||
752: 'c5 ec f8 8d e8 97 fd 57 fe d3 01 70 1b 82 a2 59',
|
||||
768: 'ec cb e1 3d e1 fc c9 1c 11 a0 b2 6c 0b c8 fa 4d',
|
||||
1008: 'e7 a7 25 74 f8 78 2a e2 6a ab cf 9e bc d6 60 65',
|
||||
1024: 'bd f0 32 4e 60 83 dc c6 d3 ce dd 3c a8 c5 3c 16',
|
||||
1520: 'b4 01 10 c4 19 0b 56 22 a9 61 16 b0 01 7e d2 97',
|
||||
1536: 'ff a0 b5 14 64 7e c0 4f 63 06 b8 92 ae 66 11 81',
|
||||
2032: 'd0 3d 1b c0 3c d3 3d 70 df f9 fa 5d 71 96 3e bd',
|
||||
2048: '8a 44 12 64 11 ea a7 8b d5 1e 8d 87 a8 87 9b f5',
|
||||
3056: 'fa be b7 60 28 ad e2 d0 e4 87 22 e4 6c 46 15 a3',
|
||||
3072: 'c0 5d 88 ab d5 03 57 f9 35 a6 3c 59 ee 53 76 23',
|
||||
4080: 'ff 38 26 5c 16 42 c1 ab e8 d3 c2 fe 5e 57 2b f8',
|
||||
4096: 'a3 6a 4c 30 1a e8 ac 13 61 0c cb c1 22 56 ca cc'
|
||||
}
|
||||
),
|
||||
# Page 6
|
||||
(
|
||||
'0102030405060708090a0b0c0d0e0f101112131415161718',
|
||||
{
|
||||
0: '05 95 e5 7f e5 f0 bb 3c 70 6e da c8 a4 b2 db 11',
|
||||
16: 'df de 31 34 4a 1a f7 69 c7 4f 07 0a ee 9e 23 26',
|
||||
240: 'b0 6b 9b 1e 19 5d 13 d8 f4 a7 99 5c 45 53 ac 05',
|
||||
256: '6b d2 37 8e c3 41 c9 a4 2f 37 ba 79 f8 8a 32 ff',
|
||||
496: 'e7 0b ce 1d f7 64 5a db 5d 2c 41 30 21 5c 35 22',
|
||||
512: '9a 57 30 c7 fc b4 c9 af 51 ff da 89 c7 f1 ad 22',
|
||||
752: '04 85 05 5f d4 f6 f0 d9 63 ef 5a b9 a5 47 69 82',
|
||||
768: '59 1f c6 6b cd a1 0e 45 2b 03 d4 55 1f 6b 62 ac',
|
||||
1008: '27 53 cc 83 98 8a fa 3e 16 88 a1 d3 b4 2c 9a 02',
|
||||
1024: '93 61 0d 52 3d 1d 3f 00 62 b3 c2 a3 bb c7 c7 f0',
|
||||
1520: '96 c2 48 61 0a ad ed fe af 89 78 c0 3d e8 20 5a',
|
||||
1536: '0e 31 7b 3d 1c 73 b9 e9 a4 68 8f 29 6d 13 3a 19',
|
||||
2032: 'bd f0 e6 c3 cc a5 b5 b9 d5 33 b6 9c 56 ad a1 20',
|
||||
2048: '88 a2 18 b6 e2 ec e1 e6 24 6d 44 c7 59 d1 9b 10',
|
||||
3056: '68 66 39 7e 95 c1 40 53 4f 94 26 34 21 00 6e 40',
|
||||
3072: '32 cb 0a 1e 95 42 c6 b3 b8 b3 98 ab c3 b0 f1 d5',
|
||||
4080: '29 a0 b8 ae d5 4a 13 23 24 c6 2e 42 3f 54 b4 c8',
|
||||
4096: '3c b0 f3 b5 02 0a 98 b8 2a f9 fe 15 44 84 a1 68'
|
||||
}
|
||||
),
|
||||
(
|
||||
'0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20',
|
||||
{
|
||||
0: 'ea a6 bd 25 88 0b f9 3d 3f 5d 1e 4c a2 61 1d 91',
|
||||
16: 'cf a4 5c 9f 7e 71 4b 54 bd fa 80 02 7c b1 43 80',
|
||||
240: '11 4a e3 44 de d7 1b 35 f2 e6 0f eb ad 72 7f d8',
|
||||
256: '02 e1 e7 05 6b 0f 62 39 00 49 64 22 94 3e 97 b6',
|
||||
496: '91 cb 93 c7 87 96 4e 10 d9 52 7d 99 9c 6f 93 6b',
|
||||
512: '49 b1 8b 42 f8 e8 36 7c be b5 ef 10 4b a1 c7 cd',
|
||||
752: '87 08 4b 3b a7 00 ba de 95 56 10 67 27 45 b3 74',
|
||||
768: 'e7 a7 b9 e9 ec 54 0d 5f f4 3b db 12 79 2d 1b 35',
|
||||
1008: 'c7 99 b5 96 73 8f 6b 01 8c 76 c7 4b 17 59 bd 90',
|
||||
1024: '7f ec 5b fd 9f 9b 89 ce 65 48 30 90 92 d7 e9 58',
|
||||
1520: '40 f2 50 b2 6d 1f 09 6a 4a fd 4c 34 0a 58 88 15',
|
||||
1536: '3e 34 13 5c 79 db 01 02 00 76 76 51 cf 26 30 73',
|
||||
2032: 'f6 56 ab cc f8 8d d8 27 02 7b 2c e9 17 d4 64 ec',
|
||||
2048: '18 b6 25 03 bf bc 07 7f ba bb 98 f2 0d 98 ab 34',
|
||||
3056: '8a ed 95 ee 5b 0d cb fb ef 4e b2 1d 3a 3f 52 f9',
|
||||
3072: '62 5a 1a b0 0e e3 9a 53 27 34 6b dd b0 1a 9c 18',
|
||||
4080: 'a1 3a 7c 79 c7 e1 19 b5 ab 02 96 ab 28 c3 00 b9',
|
||||
4096: 'f3 e4 c0 a2 e0 2d 1d 01 f7 f0 a7 46 18 af 2b 48'
|
||||
}
|
||||
),
|
||||
# Page 7
|
||||
(
|
||||
'833222772a',
|
||||
{
|
||||
0: '80 ad 97 bd c9 73 df 8a 2e 87 9e 92 a4 97 ef da',
|
||||
16: '20 f0 60 c2 f2 e5 12 65 01 d3 d4 fe a1 0d 5f c0',
|
||||
240: 'fa a1 48 e9 90 46 18 1f ec 6b 20 85 f3 b2 0e d9',
|
||||
256: 'f0 da f5 ba b3 d5 96 83 98 57 84 6f 73 fb fe 5a',
|
||||
496: '1c 7e 2f c4 63 92 32 fe 29 75 84 b2 96 99 6b c8',
|
||||
512: '3d b9 b2 49 40 6c c8 ed ff ac 55 cc d3 22 ba 12',
|
||||
752: 'e4 f9 f7 e0 06 61 54 bb d1 25 b7 45 56 9b c8 97',
|
||||
768: '75 d5 ef 26 2b 44 c4 1a 9c f6 3a e1 45 68 e1 b9',
|
||||
1008: '6d a4 53 db f8 1e 82 33 4a 3d 88 66 cb 50 a1 e3',
|
||||
1024: '78 28 d0 74 11 9c ab 5c 22 b2 94 d7 a9 bf a0 bb',
|
||||
1520: 'ad b8 9c ea 9a 15 fb e6 17 29 5b d0 4b 8c a0 5c',
|
||||
1536: '62 51 d8 7f d4 aa ae 9a 7e 4a d5 c2 17 d3 f3 00',
|
||||
2032: 'e7 11 9b d6 dd 9b 22 af e8 f8 95 85 43 28 81 e2',
|
||||
2048: '78 5b 60 fd 7e c4 e9 fc b6 54 5f 35 0d 66 0f ab',
|
||||
3056: 'af ec c0 37 fd b7 b0 83 8e b3 d7 0b cd 26 83 82',
|
||||
3072: 'db c1 a7 b4 9d 57 35 8c c9 fa 6d 61 d7 3b 7c f0',
|
||||
4080: '63 49 d1 26 a3 7a fc ba 89 79 4f 98 04 91 4f dc',
|
||||
4096: 'bf 42 c3 01 8c 2f 7c 66 bf de 52 49 75 76 81 15'
|
||||
}
|
||||
),
|
||||
(
|
||||
'1910833222772a',
|
||||
{
|
||||
0: 'bc 92 22 db d3 27 4d 8f c6 6d 14 cc bd a6 69 0b',
|
||||
16: '7a e6 27 41 0c 9a 2b e6 93 df 5b b7 48 5a 63 e3',
|
||||
240: '3f 09 31 aa 03 de fb 30 0f 06 01 03 82 6f 2a 64',
|
||||
256: 'be aa 9e c8 d5 9b b6 81 29 f3 02 7c 96 36 11 81',
|
||||
496: '74 e0 4d b4 6d 28 64 8d 7d ee 8a 00 64 b0 6c fe',
|
||||
512: '9b 5e 81 c6 2f e0 23 c5 5b e4 2f 87 bb f9 32 b8',
|
||||
752: 'ce 17 8f c1 82 6e fe cb c1 82 f5 79 99 a4 61 40',
|
||||
768: '8b df 55 cd 55 06 1c 06 db a6 be 11 de 4a 57 8a',
|
||||
1008: '62 6f 5f 4d ce 65 25 01 f3 08 7d 39 c9 2c c3 49',
|
||||
1024: '42 da ac 6a 8f 9a b9 a7 fd 13 7c 60 37 82 56 82',
|
||||
1520: 'cc 03 fd b7 91 92 a2 07 31 2f 53 f5 d4 dc 33 d9',
|
||||
1536: 'f7 0f 14 12 2a 1c 98 a3 15 5d 28 b8 a0 a8 a4 1d',
|
||||
2032: '2a 3a 30 7a b2 70 8a 9c 00 fe 0b 42 f9 c2 d6 a1',
|
||||
2048: '86 26 17 62 7d 22 61 ea b0 b1 24 65 97 ca 0a e9',
|
||||
3056: '55 f8 77 ce 4f 2e 1d db bf 8e 13 e2 cd e0 fd c8',
|
||||
3072: '1b 15 56 cb 93 5f 17 33 37 70 5f bb 5d 50 1f c1',
|
||||
4080: 'ec d0 e9 66 02 be 7f 8d 50 92 81 6c cc f2 c2 e9',
|
||||
4096: '02 78 81 fa b4 99 3a 1c 26 20 24 a9 4f ff 3f 61'
|
||||
}
|
||||
),
|
||||
# Page 8
|
||||
(
|
||||
'641910833222772a',
|
||||
{
|
||||
0: 'bb f6 09 de 94 13 17 2d 07 66 0c b6 80 71 69 26',
|
||||
16: '46 10 1a 6d ab 43 11 5d 6c 52 2b 4f e9 36 04 a9',
|
||||
240: 'cb e1 ff f2 1c 96 f3 ee f6 1e 8f e0 54 2c bd f0',
|
||||
256: '34 79 38 bf fa 40 09 c5 12 cf b4 03 4b 0d d1 a7',
|
||||
496: '78 67 a7 86 d0 0a 71 47 90 4d 76 dd f1 e5 20 e3',
|
||||
512: '8d 3e 9e 1c ae fc cc b3 fb f8 d1 8f 64 12 0b 32',
|
||||
752: '94 23 37 f8 fd 76 f0 fa e8 c5 2d 79 54 81 06 72',
|
||||
768: 'b8 54 8c 10 f5 16 67 f6 e6 0e 18 2f a1 9b 30 f7',
|
||||
1008: '02 11 c7 c6 19 0c 9e fd 12 37 c3 4c 8f 2e 06 c4',
|
||||
1024: 'bd a6 4f 65 27 6d 2a ac b8 f9 02 12 20 3a 80 8e',
|
||||
1520: 'bd 38 20 f7 32 ff b5 3e c1 93 e7 9d 33 e2 7c 73',
|
||||
1536: 'd0 16 86 16 86 19 07 d4 82 e3 6c da c8 cf 57 49',
|
||||
2032: '97 b0 f0 f2 24 b2 d2 31 71 14 80 8f b0 3a f7 a0',
|
||||
2048: 'e5 96 16 e4 69 78 79 39 a0 63 ce ea 9a f9 56 d1',
|
||||
3056: 'c4 7e 0d c1 66 09 19 c1 11 01 20 8f 9e 69 aa 1f',
|
||||
3072: '5a e4 f1 28 96 b8 37 9a 2a ad 89 b5 b5 53 d6 b0',
|
||||
4080: '6b 6b 09 8d 0c 29 3b c2 99 3d 80 bf 05 18 b6 d9',
|
||||
4096: '81 70 cc 3c cd 92 a6 98 62 1b 93 9d d3 8f e7 b9'
|
||||
}
|
||||
),
|
||||
(
|
||||
'8b37641910833222772a',
|
||||
{
|
||||
0: 'ab 65 c2 6e dd b2 87 60 0d b2 fd a1 0d 1e 60 5c',
|
||||
16: 'bb 75 90 10 c2 96 58 f2 c7 2d 93 a2 d1 6d 29 30',
|
||||
240: 'b9 01 e8 03 6e d1 c3 83 cd 3c 4c 4d d0 a6 ab 05',
|
||||
256: '3d 25 ce 49 22 92 4c 55 f0 64 94 33 53 d7 8a 6c',
|
||||
496: '12 c1 aa 44 bb f8 7e 75 e6 11 f6 9b 2c 38 f4 9b',
|
||||
512: '28 f2 b3 43 4b 65 c0 98 77 47 00 44 c6 ea 17 0d',
|
||||
752: 'bd 9e f8 22 de 52 88 19 61 34 cf 8a f7 83 93 04',
|
||||
768: '67 55 9c 23 f0 52 15 84 70 a2 96 f7 25 73 5a 32',
|
||||
1008: '8b ab 26 fb c2 c1 2b 0f 13 e2 ab 18 5e ab f2 41',
|
||||
1024: '31 18 5a 6d 69 6f 0c fa 9b 42 80 8b 38 e1 32 a2',
|
||||
1520: '56 4d 3d ae 18 3c 52 34 c8 af 1e 51 06 1c 44 b5',
|
||||
1536: '3c 07 78 a7 b5 f7 2d 3c 23 a3 13 5c 7d 67 b9 f4',
|
||||
2032: 'f3 43 69 89 0f cf 16 fb 51 7d ca ae 44 63 b2 dd',
|
||||
2048: '02 f3 1c 81 e8 20 07 31 b8 99 b0 28 e7 91 bf a7',
|
||||
3056: '72 da 64 62 83 22 8c 14 30 08 53 70 17 95 61 6f',
|
||||
3072: '4e 0a 8c 6f 79 34 a7 88 e2 26 5e 81 d6 d0 c8 f4',
|
||||
4080: '43 8d d5 ea fe a0 11 1b 6f 36 b4 b9 38 da 2a 68',
|
||||
4096: '5f 6b fc 73 81 58 74 d9 71 00 f0 86 97 93 57 d8'
|
||||
}
|
||||
),
|
||||
# Page 9
|
||||
(
|
||||
'ebb46227c6cc8b37641910833222772a',
|
||||
{
|
||||
0: '72 0c 94 b6 3e df 44 e1 31 d9 50 ca 21 1a 5a 30',
|
||||
16: 'c3 66 fd ea cf 9c a8 04 36 be 7c 35 84 24 d2 0b',
|
||||
240: 'b3 39 4a 40 aa bf 75 cb a4 22 82 ef 25 a0 05 9f',
|
||||
256: '48 47 d8 1d a4 94 2d bc 24 9d ef c4 8c 92 2b 9f',
|
||||
496: '08 12 8c 46 9f 27 53 42 ad da 20 2b 2b 58 da 95',
|
||||
512: '97 0d ac ef 40 ad 98 72 3b ac 5d 69 55 b8 17 61',
|
||||
752: '3c b8 99 93 b0 7b 0c ed 93 de 13 d2 a1 10 13 ac',
|
||||
768: 'ef 2d 67 6f 15 45 c2 c1 3d c6 80 a0 2f 4a db fe',
|
||||
1008: 'b6 05 95 51 4f 24 bc 9f e5 22 a6 ca d7 39 36 44',
|
||||
1024: 'b5 15 a8 c5 01 17 54 f5 90 03 05 8b db 81 51 4e',
|
||||
1520: '3c 70 04 7e 8c bc 03 8e 3b 98 20 db 60 1d a4 95',
|
||||
1536: '11 75 da 6e e7 56 de 46 a5 3e 2b 07 56 60 b7 70',
|
||||
2032: '00 a5 42 bb a0 21 11 cc 2c 65 b3 8e bd ba 58 7e',
|
||||
2048: '58 65 fd bb 5b 48 06 41 04 e8 30 b3 80 f2 ae de',
|
||||
3056: '34 b2 1a d2 ad 44 e9 99 db 2d 7f 08 63 f0 d9 b6',
|
||||
3072: '84 a9 21 8f c3 6e 8a 5f 2c cf be ae 53 a2 7d 25',
|
||||
4080: 'a2 22 1a 11 b8 33 cc b4 98 a5 95 40 f0 54 5f 4a',
|
||||
4096: '5b be b4 78 7d 59 e5 37 3f db ea 6c 6f 75 c2 9b'
|
||||
}
|
||||
),
|
||||
(
|
||||
'c109163908ebe51debb46227c6cc8b37641910833222772a',
|
||||
{
|
||||
0: '54 b6 4e 6b 5a 20 b5 e2 ec 84 59 3d c7 98 9d a7',
|
||||
16: 'c1 35 ee e2 37 a8 54 65 ff 97 dc 03 92 4f 45 ce',
|
||||
240: 'cf cc 92 2f b4 a1 4a b4 5d 61 75 aa bb f2 d2 01',
|
||||
256: '83 7b 87 e2 a4 46 ad 0e f7 98 ac d0 2b 94 12 4f',
|
||||
496: '17 a6 db d6 64 92 6a 06 36 b3 f4 c3 7a 4f 46 94',
|
||||
512: '4a 5f 9f 26 ae ee d4 d4 a2 5f 63 2d 30 52 33 d9',
|
||||
752: '80 a3 d0 1e f0 0c 8e 9a 42 09 c1 7f 4e eb 35 8c',
|
||||
768: 'd1 5e 7d 5f fa aa bc 02 07 bf 20 0a 11 77 93 a2',
|
||||
1008: '34 96 82 bf 58 8e aa 52 d0 aa 15 60 34 6a ea fa',
|
||||
1024: 'f5 85 4c db 76 c8 89 e3 ad 63 35 4e 5f 72 75 e3',
|
||||
1520: '53 2c 7c ec cb 39 df 32 36 31 84 05 a4 b1 27 9c',
|
||||
1536: 'ba ef e6 d9 ce b6 51 84 22 60 e0 d1 e0 5e 3b 90',
|
||||
2032: 'e8 2d 8c 6d b5 4e 3c 63 3f 58 1c 95 2b a0 42 07',
|
||||
2048: '4b 16 e5 0a bd 38 1b d7 09 00 a9 cd 9a 62 cb 23',
|
||||
3056: '36 82 ee 33 bd 14 8b d9 f5 86 56 cd 8f 30 d9 fb',
|
||||
3072: '1e 5a 0b 84 75 04 5d 9b 20 b2 62 86 24 ed fd 9e',
|
||||
4080: '63 ed d6 84 fb 82 62 82 fe 52 8f 9c 0e 92 37 bc',
|
||||
4096: 'e4 dd 2e 98 d6 96 0f ae 0b 43 54 54 56 74 33 91'
|
||||
}
|
||||
),
|
||||
# Page 10
|
||||
(
|
||||
'1ada31d5cf688221c109163908ebe51debb46227c6cc8b37641910833222772a',
|
||||
{
|
||||
0: 'dd 5b cb 00 18 e9 22 d4 94 75 9d 7c 39 5d 02 d3',
|
||||
16: 'c8 44 6f 8f 77 ab f7 37 68 53 53 eb 89 a1 c9 eb',
|
||||
240: 'af 3e 30 f9 c0 95 04 59 38 15 15 75 c3 fb 90 98',
|
||||
256: 'f8 cb 62 74 db 99 b8 0b 1d 20 12 a9 8e d4 8f 0e',
|
||||
496: '25 c3 00 5a 1c b8 5d e0 76 25 98 39 ab 71 98 ab',
|
||||
512: '9d cb c1 83 e8 cb 99 4b 72 7b 75 be 31 80 76 9c',
|
||||
752: 'a1 d3 07 8d fa 91 69 50 3e d9 d4 49 1d ee 4e b2',
|
||||
768: '85 14 a5 49 58 58 09 6f 59 6e 4b cd 66 b1 06 65',
|
||||
1008: '5f 40 d5 9e c1 b0 3b 33 73 8e fa 60 b2 25 5d 31',
|
||||
1024: '34 77 c7 f7 64 a4 1b ac ef f9 0b f1 4f 92 b7 cc',
|
||||
1520: 'ac 4e 95 36 8d 99 b9 eb 78 b8 da 8f 81 ff a7 95',
|
||||
1536: '8c 3c 13 f8 c2 38 8b b7 3f 38 57 6e 65 b7 c4 46',
|
||||
2032: '13 c4 b9 c1 df b6 65 79 ed dd 8a 28 0b 9f 73 16',
|
||||
2048: 'dd d2 78 20 55 01 26 69 8e fa ad c6 4b 64 f6 6e',
|
||||
3056: 'f0 8f 2e 66 d2 8e d1 43 f3 a2 37 cf 9d e7 35 59',
|
||||
3072: '9e a3 6c 52 55 31 b8 80 ba 12 43 34 f5 7b 0b 70',
|
||||
4080: 'd5 a3 9e 3d fc c5 02 80 ba c4 a6 b5 aa 0d ca 7d',
|
||||
4096: '37 0b 1c 1f e6 55 91 6d 97 fd 0d 47 ca 1d 72 b8'
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
def test_keystream(self):
|
||||
for tv in self.rfc6229_data:
|
||||
key = unhexlify(b((tv[0])))
|
||||
cipher = ARC4.new(key)
|
||||
count = 0
|
||||
for offset in range(0, 4096+1, 16):
|
||||
ct = cipher.encrypt(b('\x00')*16)
|
||||
expected = tv[1].get(offset)
|
||||
if expected:
|
||||
expected = unhexlify(b(expected.replace(" ", '')))
|
||||
self.assertEqual(ct, expected)
|
||||
count += 1
|
||||
self.assertEqual(count, len(tv[1]))
|
||||
|
||||
|
||||
class Drop_Tests(unittest.TestCase):
|
||||
key = b('\xAA')*16
|
||||
data = b('\x00')*5000
|
||||
|
||||
def setUp(self):
|
||||
self.cipher = ARC4.new(self.key)
|
||||
|
||||
def test_drop256_encrypt(self):
|
||||
cipher_drop = ARC4.new(self.key, 256)
|
||||
ct_drop = cipher_drop.encrypt(self.data[:16])
|
||||
ct = self.cipher.encrypt(self.data)[256:256+16]
|
||||
self.assertEqual(ct_drop, ct)
|
||||
|
||||
def test_drop256_decrypt(self):
|
||||
cipher_drop = ARC4.new(self.key, 256)
|
||||
pt_drop = cipher_drop.decrypt(self.data[:16])
|
||||
pt = self.cipher.decrypt(self.data)[256:256+16]
|
||||
self.assertEqual(pt_drop, pt)
|
||||
|
||||
|
||||
class KeyLength(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
self.assertRaises(ValueError, ARC4.new, b'')
|
||||
self.assertRaises(ValueError, ARC4.new, b'\x00' * 257)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
from .common import make_stream_tests
|
||||
tests = make_stream_tests(ARC4, "ARC4", test_data)
|
||||
tests += list_test_cases(RFC6229_Tests)
|
||||
tests += list_test_cases(Drop_Tests)
|
||||
tests.append(KeyLength())
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def suite():
|
||||
return unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/test_Blowfish.py: Self-test for the Blowfish cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.Blowfish"""
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.Util.py3compat import bchr
|
||||
|
||||
from Crypto.Cipher import Blowfish
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key) tuples.
|
||||
test_data = [
|
||||
# Test vectors from http://www.schneier.com/code/vectors.txt
|
||||
('0000000000000000', '4ef997456198dd78', '0000000000000000'),
|
||||
('ffffffffffffffff', '51866fd5b85ecb8a', 'ffffffffffffffff'),
|
||||
('1000000000000001', '7d856f9a613063f2', '3000000000000000'),
|
||||
('1111111111111111', '2466dd878b963c9d', '1111111111111111'),
|
||||
('1111111111111111', '61f9c3802281b096', '0123456789abcdef'),
|
||||
('0123456789abcdef', '7d0cc630afda1ec7', '1111111111111111'),
|
||||
('0000000000000000', '4ef997456198dd78', '0000000000000000'),
|
||||
('0123456789abcdef', '0aceab0fc6a0a28d', 'fedcba9876543210'),
|
||||
('01a1d6d039776742', '59c68245eb05282b', '7ca110454a1a6e57'),
|
||||
('5cd54ca83def57da', 'b1b8cc0b250f09a0', '0131d9619dc1376e'),
|
||||
('0248d43806f67172', '1730e5778bea1da4', '07a1133e4a0b2686'),
|
||||
('51454b582ddf440a', 'a25e7856cf2651eb', '3849674c2602319e'),
|
||||
('42fd443059577fa2', '353882b109ce8f1a', '04b915ba43feb5b6'),
|
||||
('059b5e0851cf143a', '48f4d0884c379918', '0113b970fd34f2ce'),
|
||||
('0756d8e0774761d2', '432193b78951fc98', '0170f175468fb5e6'),
|
||||
('762514b829bf486a', '13f04154d69d1ae5', '43297fad38e373fe'),
|
||||
('3bdd119049372802', '2eedda93ffd39c79', '07a7137045da2a16'),
|
||||
('26955f6835af609a', 'd887e0393c2da6e3', '04689104c2fd3b2f'),
|
||||
('164d5e404f275232', '5f99d04f5b163969', '37d06bb516cb7546'),
|
||||
('6b056e18759f5cca', '4a057a3b24d3977b', '1f08260d1ac2465e'),
|
||||
('004bd6ef09176062', '452031c1e4fada8e', '584023641aba6176'),
|
||||
('480d39006ee762f2', '7555ae39f59b87bd', '025816164629b007'),
|
||||
('437540c8698f3cfa', '53c55f9cb49fc019', '49793ebc79b3258f'),
|
||||
('072d43a077075292', '7a8e7bfa937e89a3', '4fb05e1515ab73a7'),
|
||||
('02fe55778117f12a', 'cf9c5d7a4986adb5', '49e95d6d4ca229bf'),
|
||||
('1d9d5c5018f728c2', 'd1abb290658bc778', '018310dc409b26d6'),
|
||||
('305532286d6f295a', '55cb3774d13ef201', '1c587f1c13924fef'),
|
||||
('0123456789abcdef', 'fa34ec4847b268b2', '0101010101010101'),
|
||||
('0123456789abcdef', 'a790795108ea3cae', '1f1f1f1f0e0e0e0e'),
|
||||
('0123456789abcdef', 'c39e072d9fac631d', 'e0fee0fef1fef1fe'),
|
||||
('ffffffffffffffff', '014933e0cdaff6e4', '0000000000000000'),
|
||||
('0000000000000000', 'f21e9a77b71c49bc', 'ffffffffffffffff'),
|
||||
('0000000000000000', '245946885754369a', '0123456789abcdef'),
|
||||
('ffffffffffffffff', '6b5c5a9c5d9e0a5a', 'fedcba9876543210'),
|
||||
#('fedcba9876543210', 'f9ad597c49db005e', 'f0'),
|
||||
#('fedcba9876543210', 'e91d21c1d961a6d6', 'f0e1'),
|
||||
#('fedcba9876543210', 'e9c2b70a1bc65cf3', 'f0e1d2'),
|
||||
('fedcba9876543210', 'be1e639408640f05', 'f0e1d2c3'),
|
||||
('fedcba9876543210', 'b39e44481bdb1e6e', 'f0e1d2c3b4'),
|
||||
('fedcba9876543210', '9457aa83b1928c0d', 'f0e1d2c3b4a5'),
|
||||
('fedcba9876543210', '8bb77032f960629d', 'f0e1d2c3b4a596'),
|
||||
('fedcba9876543210', 'e87a244e2cc85e82', 'f0e1d2c3b4a59687'),
|
||||
('fedcba9876543210', '15750e7a4f4ec577', 'f0e1d2c3b4a5968778'),
|
||||
('fedcba9876543210', '122ba70b3ab64ae0', 'f0e1d2c3b4a596877869'),
|
||||
('fedcba9876543210', '3a833c9affc537f6', 'f0e1d2c3b4a5968778695a'),
|
||||
('fedcba9876543210', '9409da87a90f6bf2', 'f0e1d2c3b4a5968778695a4b'),
|
||||
('fedcba9876543210', '884f80625060b8b4', 'f0e1d2c3b4a5968778695a4b3c'),
|
||||
('fedcba9876543210', '1f85031c19e11968', 'f0e1d2c3b4a5968778695a4b3c2d'),
|
||||
('fedcba9876543210', '79d9373a714ca34f', 'f0e1d2c3b4a5968778695a4b3c2d1e'),
|
||||
('fedcba9876543210', '93142887ee3be15c',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f'),
|
||||
('fedcba9876543210', '03429e838ce2d14b',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f00'),
|
||||
('fedcba9876543210', 'a4299e27469ff67b',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f0011'),
|
||||
('fedcba9876543210', 'afd5aed1c1bc96a8',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f001122'),
|
||||
('fedcba9876543210', '10851c0e3858da9f',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f00112233'),
|
||||
('fedcba9876543210', 'e6f51ed79b9db21f',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344'),
|
||||
('fedcba9876543210', '64a6e14afd36b46f',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f001122334455'),
|
||||
('fedcba9876543210', '80c7d7d45a5479ad',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f00112233445566'),
|
||||
('fedcba9876543210', '05044b62fa52d080',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344556677'),
|
||||
]
|
||||
|
||||
|
||||
class KeyLength(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
self.assertRaises(ValueError, Blowfish.new, bchr(0) * 3,
|
||||
Blowfish.MODE_ECB)
|
||||
self.assertRaises(ValueError, Blowfish.new, bchr(0) * 57,
|
||||
Blowfish.MODE_ECB)
|
||||
|
||||
|
||||
class TestOutput(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
# Encrypt/Decrypt data and test output parameter
|
||||
|
||||
cipher = Blowfish.new(b'4'*16, Blowfish.MODE_ECB)
|
||||
|
||||
pt = b'5' * 16
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = bytearray(16)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
output = memoryview(bytearray(16))
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16)
|
||||
|
||||
shorter_output = bytearray(7)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
from .common import make_block_tests
|
||||
tests = make_block_tests(Blowfish, "Blowfish", test_data)
|
||||
tests.append(KeyLength())
|
||||
tests += [TestOutput()]
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/CAST.py: Self-test for the CAST-128 (CAST5) cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.CAST"""
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.Util.py3compat import bchr
|
||||
|
||||
from Crypto.Cipher import CAST
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key) tuples.
|
||||
test_data = [
|
||||
# Test vectors from RFC 2144, B.1
|
||||
('0123456789abcdef', '238b4fe5847e44b2',
|
||||
'0123456712345678234567893456789a',
|
||||
'128-bit key'),
|
||||
|
||||
('0123456789abcdef', 'eb6a711a2c02271b',
|
||||
'01234567123456782345',
|
||||
'80-bit key'),
|
||||
|
||||
('0123456789abcdef', '7ac816d16e9b302e',
|
||||
'0123456712',
|
||||
'40-bit key'),
|
||||
]
|
||||
|
||||
|
||||
class KeyLength(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
self.assertRaises(ValueError, CAST.new, bchr(0) * 4, CAST.MODE_ECB)
|
||||
self.assertRaises(ValueError, CAST.new, bchr(0) * 17, CAST.MODE_ECB)
|
||||
|
||||
|
||||
class TestOutput(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
# Encrypt/Decrypt data and test output parameter
|
||||
|
||||
cipher = CAST.new(b'4'*16, CAST.MODE_ECB)
|
||||
|
||||
pt = b'5' * 16
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = bytearray(16)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
output = memoryview(bytearray(16))
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16)
|
||||
|
||||
shorter_output = bytearray(7)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
from .common import make_block_tests
|
||||
|
||||
tests = make_block_tests(CAST, "CAST", test_data)
|
||||
tests.append(KeyLength())
|
||||
tests.append(TestOutput())
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,556 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.SelfTest.loader import load_test_vectors
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.Util.py3compat import tobytes, is_string
|
||||
from Crypto.Cipher import AES, DES3, DES
|
||||
from Crypto.Hash import SHAKE128
|
||||
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
class BlockChainingTests(unittest.TestCase):
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
key_192 = get_tag_random("key_192", 24)
|
||||
iv_128 = get_tag_random("iv_128", 16)
|
||||
iv_64 = get_tag_random("iv_64", 8)
|
||||
data_128 = get_tag_random("data_128", 16)
|
||||
|
||||
def test_loopback_128(self):
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
pt = get_tag_random("plaintext", 16 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_loopback_64(self):
|
||||
cipher = DES3.new(self.key_192, self.des3_mode, self.iv_64)
|
||||
pt = get_tag_random("plaintext", 8 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = DES3.new(self.key_192, self.des3_mode, self.iv_64)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_iv(self):
|
||||
# If not passed, the iv is created randomly
|
||||
cipher = AES.new(self.key_128, self.aes_mode)
|
||||
iv1 = cipher.iv
|
||||
cipher = AES.new(self.key_128, self.aes_mode)
|
||||
iv2 = cipher.iv
|
||||
self.assertNotEqual(iv1, iv2)
|
||||
self.assertEqual(len(iv1), 16)
|
||||
|
||||
# IV can be passed in uppercase or lowercase
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
ct = cipher.encrypt(self.data_128)
|
||||
|
||||
cipher = AES.new(self.key_128, self.aes_mode, iv=self.iv_128)
|
||||
self.assertEqual(ct, cipher.encrypt(self.data_128))
|
||||
|
||||
cipher = AES.new(self.key_128, self.aes_mode, IV=self.iv_128)
|
||||
self.assertEqual(ct, cipher.encrypt(self.data_128))
|
||||
|
||||
def test_iv_must_be_bytes(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, self.aes_mode,
|
||||
iv = u'test1234567890-*')
|
||||
|
||||
def test_only_one_iv(self):
|
||||
# Only one IV/iv keyword allowed
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, self.aes_mode,
|
||||
iv=self.iv_128, IV=self.iv_128)
|
||||
|
||||
def test_iv_with_matching_length(self):
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, self.aes_mode,
|
||||
b"")
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, self.aes_mode,
|
||||
self.iv_128[:15])
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, self.aes_mode,
|
||||
self.iv_128 + b"0")
|
||||
|
||||
def test_block_size_128(self):
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
self.assertEqual(cipher.block_size, AES.block_size)
|
||||
|
||||
def test_block_size_64(self):
|
||||
cipher = DES3.new(self.key_192, self.des3_mode, self.iv_64)
|
||||
self.assertEqual(cipher.block_size, DES3.block_size)
|
||||
|
||||
def test_unaligned_data_128(self):
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
for wrong_length in range(1,16):
|
||||
self.assertRaises(ValueError, cipher.encrypt, b"5" * wrong_length)
|
||||
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
for wrong_length in range(1,16):
|
||||
self.assertRaises(ValueError, cipher.decrypt, b"5" * wrong_length)
|
||||
|
||||
def test_unaligned_data_64(self):
|
||||
cipher = DES3.new(self.key_192, self.des3_mode, self.iv_64)
|
||||
for wrong_length in range(1,8):
|
||||
self.assertRaises(ValueError, cipher.encrypt, b"5" * wrong_length)
|
||||
|
||||
cipher = DES3.new(self.key_192, self.des3_mode, self.iv_64)
|
||||
for wrong_length in range(1,8):
|
||||
self.assertRaises(ValueError, cipher.decrypt, b"5" * wrong_length)
|
||||
|
||||
def test_IV_iv_attributes(self):
|
||||
data = get_tag_random("data", 16 * 100)
|
||||
for func in "encrypt", "decrypt":
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
getattr(cipher, func)(data)
|
||||
self.assertEqual(cipher.iv, self.iv_128)
|
||||
self.assertEqual(cipher.IV, self.iv_128)
|
||||
|
||||
def test_unknown_parameters(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, self.aes_mode,
|
||||
self.iv_128, 7)
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, self.aes_mode,
|
||||
iv=self.iv_128, unknown=7)
|
||||
# But some are only known by the base cipher (e.g. use_aesni consumed by the AES module)
|
||||
AES.new(self.key_128, self.aes_mode, iv=self.iv_128, use_aesni=False)
|
||||
|
||||
def test_null_encryption_decryption(self):
|
||||
for func in "encrypt", "decrypt":
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
result = getattr(cipher, func)(b"")
|
||||
self.assertEqual(result, b"")
|
||||
|
||||
def test_either_encrypt_or_decrypt(self):
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
cipher.encrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.decrypt, b"")
|
||||
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
cipher.decrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.encrypt, b"")
|
||||
|
||||
def test_data_must_be_bytes(self):
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*')
|
||||
|
||||
cipher = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*')
|
||||
|
||||
def test_bytearray(self):
|
||||
data = b"1" * 128
|
||||
data_ba = bytearray(data)
|
||||
|
||||
# Encrypt
|
||||
key_ba = bytearray(self.key_128)
|
||||
iv_ba = bytearray(self.iv_128)
|
||||
|
||||
cipher1 = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
ref1 = cipher1.encrypt(data)
|
||||
|
||||
cipher2 = AES.new(key_ba, self.aes_mode, iv_ba)
|
||||
key_ba[:3] = b'\xFF\xFF\xFF'
|
||||
iv_ba[:3] = b'\xFF\xFF\xFF'
|
||||
ref2 = cipher2.encrypt(data_ba)
|
||||
|
||||
self.assertEqual(ref1, ref2)
|
||||
self.assertEqual(cipher1.iv, cipher2.iv)
|
||||
|
||||
# Decrypt
|
||||
key_ba = bytearray(self.key_128)
|
||||
iv_ba = bytearray(self.iv_128)
|
||||
|
||||
cipher3 = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
ref3 = cipher3.decrypt(data)
|
||||
|
||||
cipher4 = AES.new(key_ba, self.aes_mode, iv_ba)
|
||||
key_ba[:3] = b'\xFF\xFF\xFF'
|
||||
iv_ba[:3] = b'\xFF\xFF\xFF'
|
||||
ref4 = cipher4.decrypt(data_ba)
|
||||
|
||||
self.assertEqual(ref3, ref4)
|
||||
|
||||
def test_memoryview(self):
|
||||
data = b"1" * 128
|
||||
data_mv = memoryview(bytearray(data))
|
||||
|
||||
# Encrypt
|
||||
key_mv = memoryview(bytearray(self.key_128))
|
||||
iv_mv = memoryview(bytearray(self.iv_128))
|
||||
|
||||
cipher1 = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
ref1 = cipher1.encrypt(data)
|
||||
|
||||
cipher2 = AES.new(key_mv, self.aes_mode, iv_mv)
|
||||
key_mv[:3] = b'\xFF\xFF\xFF'
|
||||
iv_mv[:3] = b'\xFF\xFF\xFF'
|
||||
ref2 = cipher2.encrypt(data_mv)
|
||||
|
||||
self.assertEqual(ref1, ref2)
|
||||
self.assertEqual(cipher1.iv, cipher2.iv)
|
||||
|
||||
# Decrypt
|
||||
key_mv = memoryview(bytearray(self.key_128))
|
||||
iv_mv = memoryview(bytearray(self.iv_128))
|
||||
|
||||
cipher3 = AES.new(self.key_128, self.aes_mode, self.iv_128)
|
||||
ref3 = cipher3.decrypt(data)
|
||||
|
||||
cipher4 = AES.new(key_mv, self.aes_mode, iv_mv)
|
||||
key_mv[:3] = b'\xFF\xFF\xFF'
|
||||
iv_mv[:3] = b'\xFF\xFF\xFF'
|
||||
ref4 = cipher4.decrypt(data_mv)
|
||||
|
||||
self.assertEqual(ref3, ref4)
|
||||
|
||||
def test_output_param(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = bytearray(128)
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
|
||||
def test_output_param_same_buffer(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
pt_ba = bytearray(pt)
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
res = cipher.encrypt(pt_ba, output=pt_ba)
|
||||
self.assertEqual(ct, pt_ba)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
ct_ba = bytearray(ct)
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
res = cipher.decrypt(ct_ba, output=ct_ba)
|
||||
self.assertEqual(pt, ct_ba)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
|
||||
def test_output_param_memoryview(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = memoryview(bytearray(128))
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
def test_output_param_neg(self):
|
||||
LEN_PT = 128
|
||||
|
||||
pt = b'5' * LEN_PT
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0' * LEN_PT)
|
||||
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0' * LEN_PT)
|
||||
|
||||
shorter_output = bytearray(LEN_PT - 1)
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
class CbcTests(BlockChainingTests):
|
||||
aes_mode = AES.MODE_CBC
|
||||
des3_mode = DES3.MODE_CBC
|
||||
|
||||
|
||||
class NistBlockChainingVectors(unittest.TestCase):
|
||||
|
||||
def _do_kat_aes_test(self, file_name):
|
||||
|
||||
test_vectors = load_test_vectors(("Cipher", "AES"),
|
||||
file_name,
|
||||
"AES CBC KAT",
|
||||
{ "count" : lambda x: int(x) } )
|
||||
if test_vectors is None:
|
||||
return
|
||||
|
||||
direction = None
|
||||
for tv in test_vectors:
|
||||
|
||||
# The test vector file contains some directive lines
|
||||
if is_string(tv):
|
||||
direction = tv
|
||||
continue
|
||||
|
||||
self.description = tv.desc
|
||||
|
||||
cipher = AES.new(tv.key, self.aes_mode, tv.iv)
|
||||
if direction == "[ENCRYPT]":
|
||||
self.assertEqual(cipher.encrypt(tv.plaintext), tv.ciphertext)
|
||||
elif direction == "[DECRYPT]":
|
||||
self.assertEqual(cipher.decrypt(tv.ciphertext), tv.plaintext)
|
||||
else:
|
||||
assert False
|
||||
|
||||
# See Section 6.4.2 in AESAVS
|
||||
def _do_mct_aes_test(self, file_name):
|
||||
|
||||
test_vectors = load_test_vectors(("Cipher", "AES"),
|
||||
file_name,
|
||||
"AES CBC Montecarlo",
|
||||
{ "count" : lambda x: int(x) } )
|
||||
if test_vectors is None:
|
||||
return
|
||||
|
||||
direction = None
|
||||
for tv in test_vectors:
|
||||
|
||||
# The test vector file contains some directive lines
|
||||
if is_string(tv):
|
||||
direction = tv
|
||||
continue
|
||||
|
||||
self.description = tv.desc
|
||||
cipher = AES.new(tv.key, self.aes_mode, tv.iv)
|
||||
|
||||
if direction == '[ENCRYPT]':
|
||||
cts = [ tv.iv ]
|
||||
for count in range(1000):
|
||||
cts.append(cipher.encrypt(tv.plaintext))
|
||||
tv.plaintext = cts[-2]
|
||||
self.assertEqual(cts[-1], tv.ciphertext)
|
||||
elif direction == '[DECRYPT]':
|
||||
pts = [ tv.iv]
|
||||
for count in range(1000):
|
||||
pts.append(cipher.decrypt(tv.ciphertext))
|
||||
tv.ciphertext = pts[-2]
|
||||
self.assertEqual(pts[-1], tv.plaintext)
|
||||
else:
|
||||
assert False
|
||||
|
||||
def _do_tdes_test(self, file_name):
|
||||
|
||||
test_vectors = load_test_vectors(("Cipher", "TDES"),
|
||||
file_name,
|
||||
"TDES CBC KAT",
|
||||
{ "count" : lambda x: int(x) } )
|
||||
if test_vectors is None:
|
||||
return
|
||||
|
||||
direction = None
|
||||
for tv in test_vectors:
|
||||
|
||||
# The test vector file contains some directive lines
|
||||
if is_string(tv):
|
||||
direction = tv
|
||||
continue
|
||||
|
||||
self.description = tv.desc
|
||||
if hasattr(tv, "keys"):
|
||||
cipher = DES.new(tv.keys, self.des_mode, tv.iv)
|
||||
else:
|
||||
if tv.key1 != tv.key3:
|
||||
key = tv.key1 + tv.key2 + tv.key3 # Option 3
|
||||
else:
|
||||
key = tv.key1 + tv.key2 # Option 2
|
||||
cipher = DES3.new(key, self.des3_mode, tv.iv)
|
||||
|
||||
if direction == "[ENCRYPT]":
|
||||
self.assertEqual(cipher.encrypt(tv.plaintext), tv.ciphertext)
|
||||
elif direction == "[DECRYPT]":
|
||||
self.assertEqual(cipher.decrypt(tv.ciphertext), tv.plaintext)
|
||||
else:
|
||||
assert False
|
||||
|
||||
|
||||
class NistCbcVectors(NistBlockChainingVectors):
|
||||
aes_mode = AES.MODE_CBC
|
||||
des_mode = DES.MODE_CBC
|
||||
des3_mode = DES3.MODE_CBC
|
||||
|
||||
|
||||
# Create one test method per file
|
||||
nist_aes_kat_mmt_files = (
|
||||
# KAT
|
||||
"CBCGFSbox128.rsp",
|
||||
"CBCGFSbox192.rsp",
|
||||
"CBCGFSbox256.rsp",
|
||||
"CBCKeySbox128.rsp",
|
||||
"CBCKeySbox192.rsp",
|
||||
"CBCKeySbox256.rsp",
|
||||
"CBCVarKey128.rsp",
|
||||
"CBCVarKey192.rsp",
|
||||
"CBCVarKey256.rsp",
|
||||
"CBCVarTxt128.rsp",
|
||||
"CBCVarTxt192.rsp",
|
||||
"CBCVarTxt256.rsp",
|
||||
# MMT
|
||||
"CBCMMT128.rsp",
|
||||
"CBCMMT192.rsp",
|
||||
"CBCMMT256.rsp",
|
||||
)
|
||||
nist_aes_mct_files = (
|
||||
"CBCMCT128.rsp",
|
||||
"CBCMCT192.rsp",
|
||||
"CBCMCT256.rsp",
|
||||
)
|
||||
|
||||
for file_name in nist_aes_kat_mmt_files:
|
||||
def new_func(self, file_name=file_name):
|
||||
self._do_kat_aes_test(file_name)
|
||||
setattr(NistCbcVectors, "test_AES_" + file_name, new_func)
|
||||
|
||||
for file_name in nist_aes_mct_files:
|
||||
def new_func(self, file_name=file_name):
|
||||
self._do_mct_aes_test(file_name)
|
||||
setattr(NistCbcVectors, "test_AES_" + file_name, new_func)
|
||||
del file_name, new_func
|
||||
|
||||
nist_tdes_files = (
|
||||
"TCBCMMT2.rsp", # 2TDES
|
||||
"TCBCMMT3.rsp", # 3TDES
|
||||
"TCBCinvperm.rsp", # Single DES
|
||||
"TCBCpermop.rsp",
|
||||
"TCBCsubtab.rsp",
|
||||
"TCBCvarkey.rsp",
|
||||
"TCBCvartext.rsp",
|
||||
)
|
||||
|
||||
for file_name in nist_tdes_files:
|
||||
def new_func(self, file_name=file_name):
|
||||
self._do_tdes_test(file_name)
|
||||
setattr(NistCbcVectors, "test_TDES_" + file_name, new_func)
|
||||
|
||||
# END OF NIST CBC TEST VECTORS
|
||||
|
||||
|
||||
class SP800TestVectors(unittest.TestCase):
|
||||
"""Class exercising the CBC test vectors found in Section F.2
|
||||
of NIST SP 800-3A"""
|
||||
|
||||
def test_aes_128(self):
|
||||
key = '2b7e151628aed2a6abf7158809cf4f3c'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = '7649abac8119b246cee98e9b12e9197d' +\
|
||||
'5086cb9b507219ee95db113a917678b2' +\
|
||||
'73bed6b8e3c1743b7116e69e22229516' +\
|
||||
'3ff1caa1681fac09120eca307586e1a7'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
def test_aes_192(self):
|
||||
key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = '4f021db243bc633d7178183a9fa071e8' +\
|
||||
'b4d9ada9ad7dedf4e5e738763f69145a' +\
|
||||
'571b242012fb7ae07fa9baac3df102e0' +\
|
||||
'08b0e27988598881d920a9e64f5615cd'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
def test_aes_256(self):
|
||||
key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = 'f58c4c04d6e5f1ba779eabfb5f7bfbd6' +\
|
||||
'9cfc4e967edb808d679f777bc6702c7d' +\
|
||||
'39f23369a9d9bacfa530e26304231461' +\
|
||||
'b2eb05e2c39be9fcda6c19078c6a9d1b'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(CbcTests)
|
||||
if config.get('slow_tests'):
|
||||
tests += list_test_cases(NistCbcVectors)
|
||||
tests += list_test_cases(SP800TestVectors)
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,936 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.SelfTest.loader import load_test_vectors_wycheproof
|
||||
from Crypto.Util.py3compat import tobytes, bchr
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Hash import SHAKE128
|
||||
|
||||
from Crypto.Util.strxor import strxor
|
||||
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
|
||||
class CcmTests(unittest.TestCase):
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
nonce_96 = get_tag_random("nonce_128", 12)
|
||||
data = get_tag_random("data", 128)
|
||||
|
||||
def test_loopback_128(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
pt = get_tag_random("plaintext", 16 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_nonce(self):
|
||||
# If not passed, the nonce is created randomly
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM)
|
||||
nonce1 = cipher.nonce
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM)
|
||||
nonce2 = cipher.nonce
|
||||
self.assertEqual(len(nonce1), 11)
|
||||
self.assertNotEqual(nonce1, nonce2)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, self.nonce_96)
|
||||
ct = cipher.encrypt(self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
self.assertEqual(ct, cipher.encrypt(self.data))
|
||||
|
||||
def test_nonce_must_be_bytes(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CCM,
|
||||
nonce=u'test12345678')
|
||||
|
||||
def test_nonce_length(self):
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CCM,
|
||||
nonce=b"")
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CCM,
|
||||
nonce=bchr(1) * 6)
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CCM,
|
||||
nonce=bchr(1) * 14)
|
||||
for x in range(7, 13 + 1):
|
||||
AES.new(self.key_128, AES.MODE_CCM, nonce=bchr(1) * x)
|
||||
|
||||
def test_block_size(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.block_size, AES.block_size)
|
||||
|
||||
def test_nonce_attribute(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.nonce, self.nonce_96)
|
||||
|
||||
# By default, a 11 bytes long nonce is randomly generated
|
||||
nonce1 = AES.new(self.key_128, AES.MODE_CCM).nonce
|
||||
nonce2 = AES.new(self.key_128, AES.MODE_CCM).nonce
|
||||
self.assertEqual(len(nonce1), 11)
|
||||
self.assertNotEqual(nonce1, nonce2)
|
||||
|
||||
def test_unknown_parameters(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CCM,
|
||||
self.nonce_96, 7)
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96, unknown=7)
|
||||
|
||||
# But some are only known by the base cipher
|
||||
# (e.g. use_aesni consumed by the AES module)
|
||||
AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
use_aesni=False)
|
||||
|
||||
def test_null_encryption_decryption(self):
|
||||
for func in "encrypt", "decrypt":
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
result = getattr(cipher, func)(b"")
|
||||
self.assertEqual(result, b"")
|
||||
|
||||
def test_either_encrypt_or_decrypt(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.encrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.decrypt, b"")
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.decrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.encrypt, b"")
|
||||
|
||||
def test_data_must_be_bytes(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*')
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*')
|
||||
|
||||
def test_mac_len(self):
|
||||
# Invalid MAC length
|
||||
for mac_len in range(3, 17 + 1, 2):
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96, mac_len=mac_len)
|
||||
|
||||
# Valid MAC length
|
||||
for mac_len in range(4, 16 + 1, 2):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
mac_len=mac_len)
|
||||
_, mac = cipher.encrypt_and_digest(self.data)
|
||||
self.assertEqual(len(mac), mac_len)
|
||||
|
||||
# Default MAC length
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
_, mac = cipher.encrypt_and_digest(self.data)
|
||||
self.assertEqual(len(mac), 16)
|
||||
|
||||
def test_invalid_mac(self):
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
invalid_mac = strxor_c(mac, 0x01)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct,
|
||||
invalid_mac)
|
||||
|
||||
def test_hex_mac(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
mac_hex = cipher.hexdigest()
|
||||
self.assertEqual(cipher.digest(), unhexlify(mac_hex))
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.hexverify(mac_hex)
|
||||
|
||||
def test_longer_assoc_data_than_declared(self):
|
||||
# More than zero
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
assoc_len=0)
|
||||
self.assertRaises(ValueError, cipher.update, b"1")
|
||||
|
||||
# Too large
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
assoc_len=15)
|
||||
self.assertRaises(ValueError, cipher.update, self.data)
|
||||
|
||||
def test_shorter_assoc_data_than_expected(self):
|
||||
DATA_LEN = len(self.data)
|
||||
|
||||
# With plaintext
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
assoc_len=DATA_LEN + 1)
|
||||
cipher.update(self.data)
|
||||
self.assertRaises(ValueError, cipher.encrypt, self.data)
|
||||
|
||||
# With empty plaintext
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
assoc_len=DATA_LEN + 1)
|
||||
cipher.update(self.data)
|
||||
self.assertRaises(ValueError, cipher.digest)
|
||||
|
||||
# With ciphertext
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
assoc_len=DATA_LEN + 1)
|
||||
cipher.update(self.data)
|
||||
self.assertRaises(ValueError, cipher.decrypt, self.data)
|
||||
|
||||
# With empty ciphertext
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
assoc_len=DATA_LEN + 1)
|
||||
cipher.update(self.data)
|
||||
self.assertRaises(ValueError, cipher.verify, mac)
|
||||
|
||||
def test_shorter_and_longer_plaintext_than_declared(self):
|
||||
DATA_LEN = len(self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
msg_len=DATA_LEN + 1)
|
||||
cipher.encrypt(self.data)
|
||||
self.assertRaises(ValueError, cipher.digest)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
msg_len=DATA_LEN - 1)
|
||||
self.assertRaises(ValueError, cipher.encrypt, self.data)
|
||||
|
||||
def test_shorter_ciphertext_than_declared(self):
|
||||
DATA_LEN = len(self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
msg_len=DATA_LEN + 1)
|
||||
cipher.decrypt(ct)
|
||||
self.assertRaises(ValueError, cipher.verify, mac)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
msg_len=DATA_LEN - 1)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct)
|
||||
|
||||
def test_message_chunks(self):
|
||||
# Validate that both associated data and plaintext/ciphertext
|
||||
# can be broken up in chunks of arbitrary length
|
||||
|
||||
auth_data = get_tag_random("authenticated data", 127)
|
||||
plaintext = get_tag_random("plaintext", 127)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.update(auth_data)
|
||||
ciphertext, ref_mac = cipher.encrypt_and_digest(plaintext)
|
||||
|
||||
def break_up(data, chunk_length):
|
||||
return [data[i:i+chunk_length] for i in range(0, len(data),
|
||||
chunk_length)]
|
||||
|
||||
# Encryption
|
||||
for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128:
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
msg_len=127, assoc_len=127)
|
||||
|
||||
for chunk in break_up(auth_data, chunk_length):
|
||||
cipher.update(chunk)
|
||||
pt2 = b""
|
||||
for chunk in break_up(ciphertext, chunk_length):
|
||||
pt2 += cipher.decrypt(chunk)
|
||||
self.assertEqual(plaintext, pt2)
|
||||
cipher.verify(ref_mac)
|
||||
|
||||
# Decryption
|
||||
for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128:
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96,
|
||||
msg_len=127, assoc_len=127)
|
||||
|
||||
for chunk in break_up(auth_data, chunk_length):
|
||||
cipher.update(chunk)
|
||||
ct2 = b""
|
||||
for chunk in break_up(plaintext, chunk_length):
|
||||
ct2 += cipher.encrypt(chunk)
|
||||
self.assertEqual(ciphertext, ct2)
|
||||
self.assertEqual(cipher.digest(), ref_mac)
|
||||
|
||||
def test_bytearray(self):
|
||||
|
||||
# Encrypt
|
||||
key_ba = bytearray(self.key_128)
|
||||
nonce_ba = bytearray(self.nonce_96)
|
||||
header_ba = bytearray(self.data)
|
||||
data_ba = bytearray(self.data)
|
||||
|
||||
cipher1 = AES.new(self.key_128,
|
||||
AES.MODE_CCM,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data)
|
||||
ct = cipher1.encrypt(self.data)
|
||||
tag = cipher1.digest()
|
||||
|
||||
cipher2 = AES.new(key_ba,
|
||||
AES.MODE_CCM,
|
||||
nonce=nonce_ba)
|
||||
key_ba[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_ba[:3] = b"\xFF\xFF\xFF"
|
||||
cipher2.update(header_ba)
|
||||
header_ba[:3] = b"\xFF\xFF\xFF"
|
||||
ct_test = cipher2.encrypt(data_ba)
|
||||
data_ba[:3] = b"\xFF\xFF\xFF"
|
||||
tag_test = cipher2.digest()
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key_ba = bytearray(self.key_128)
|
||||
nonce_ba = bytearray(self.nonce_96)
|
||||
header_ba = bytearray(self.data)
|
||||
del data_ba
|
||||
|
||||
cipher4 = AES.new(key_ba,
|
||||
AES.MODE_CCM,
|
||||
nonce=nonce_ba)
|
||||
key_ba[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_ba[:3] = b"\xFF\xFF\xFF"
|
||||
cipher4.update(header_ba)
|
||||
header_ba[:3] = b"\xFF\xFF\xFF"
|
||||
pt_test = cipher4.decrypt_and_verify(bytearray(ct_test), bytearray(tag_test))
|
||||
|
||||
self.assertEqual(self.data, pt_test)
|
||||
|
||||
def test_memoryview(self):
|
||||
|
||||
# Encrypt
|
||||
key_mv = memoryview(bytearray(self.key_128))
|
||||
nonce_mv = memoryview(bytearray(self.nonce_96))
|
||||
header_mv = memoryview(bytearray(self.data))
|
||||
data_mv = memoryview(bytearray(self.data))
|
||||
|
||||
cipher1 = AES.new(self.key_128,
|
||||
AES.MODE_CCM,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data)
|
||||
ct = cipher1.encrypt(self.data)
|
||||
tag = cipher1.digest()
|
||||
|
||||
cipher2 = AES.new(key_mv,
|
||||
AES.MODE_CCM,
|
||||
nonce=nonce_mv)
|
||||
key_mv[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_mv[:3] = b"\xFF\xFF\xFF"
|
||||
cipher2.update(header_mv)
|
||||
header_mv[:3] = b"\xFF\xFF\xFF"
|
||||
ct_test = cipher2.encrypt(data_mv)
|
||||
data_mv[:3] = b"\xFF\xFF\xFF"
|
||||
tag_test = cipher2.digest()
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key_mv = memoryview(bytearray(self.key_128))
|
||||
nonce_mv = memoryview(bytearray(self.nonce_96))
|
||||
header_mv = memoryview(bytearray(self.data))
|
||||
del data_mv
|
||||
|
||||
cipher4 = AES.new(key_mv,
|
||||
AES.MODE_CCM,
|
||||
nonce=nonce_mv)
|
||||
key_mv[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_mv[:3] = b"\xFF\xFF\xFF"
|
||||
cipher4.update(header_mv)
|
||||
header_mv[:3] = b"\xFF\xFF\xFF"
|
||||
pt_test = cipher4.decrypt_and_verify(memoryview(ct_test), memoryview(tag_test))
|
||||
|
||||
self.assertEqual(self.data, pt_test)
|
||||
|
||||
def test_output_param(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(pt)
|
||||
tag = cipher.digest()
|
||||
|
||||
output = bytearray(128)
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
res, tag_out = cipher.encrypt_and_digest(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
self.assertEqual(tag, tag_out)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
res = cipher.decrypt_and_verify(ct, tag, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
def test_output_param_memoryview(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = memoryview(bytearray(128))
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
def test_output_param_neg(self):
|
||||
|
||||
pt = b'5' * 16
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16)
|
||||
|
||||
shorter_output = bytearray(15)
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
class CcmFSMTests(unittest.TestCase):
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
nonce_96 = get_tag_random("nonce_128", 12)
|
||||
data = get_tag_random("data", 16)
|
||||
|
||||
def test_valid_init_encrypt_decrypt_digest_verify(self):
|
||||
# No authenticated data, fixed plaintext
|
||||
for assoc_len in (None, 0):
|
||||
for msg_len in (None, len(self.data)):
|
||||
# Verify path INIT->ENCRYPT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96,
|
||||
assoc_len=assoc_len,
|
||||
msg_len=msg_len)
|
||||
ct = cipher.encrypt(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->DECRYPT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96,
|
||||
assoc_len=assoc_len,
|
||||
msg_len=msg_len)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_init_update_digest_verify(self):
|
||||
# No plaintext, fixed authenticated data
|
||||
for assoc_len in (None, len(self.data)):
|
||||
for msg_len in (None, 0):
|
||||
# Verify path INIT->UPDATE->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96,
|
||||
assoc_len=assoc_len,
|
||||
msg_len=msg_len)
|
||||
cipher.update(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96,
|
||||
assoc_len=assoc_len,
|
||||
msg_len=msg_len)
|
||||
cipher.update(self.data)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_full_path(self):
|
||||
# Fixed authenticated data, fixed plaintext
|
||||
for assoc_len in (None, len(self.data)):
|
||||
for msg_len in (None, len(self.data)):
|
||||
# Verify path INIT->UPDATE->ENCRYPT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96,
|
||||
assoc_len=assoc_len,
|
||||
msg_len=msg_len)
|
||||
cipher.update(self.data)
|
||||
ct = cipher.encrypt(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->DECRYPT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96,
|
||||
assoc_len=assoc_len,
|
||||
msg_len=msg_len)
|
||||
cipher.update(self.data)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_init_digest(self):
|
||||
# Verify path INIT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.digest()
|
||||
|
||||
def test_valid_init_verify(self):
|
||||
# Verify path INIT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
mac = cipher.digest()
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_multiple_encrypt_or_decrypt(self):
|
||||
# Only possible if msg_len is declared in advance
|
||||
for method_name in "encrypt", "decrypt":
|
||||
for auth_data in (None, b"333", self.data,
|
||||
self.data + b"3"):
|
||||
if auth_data is None:
|
||||
assoc_len = None
|
||||
else:
|
||||
assoc_len = len(auth_data)
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96,
|
||||
msg_len=64,
|
||||
assoc_len=assoc_len)
|
||||
if auth_data is not None:
|
||||
cipher.update(auth_data)
|
||||
method = getattr(cipher, method_name)
|
||||
method(self.data)
|
||||
method(self.data)
|
||||
method(self.data)
|
||||
method(self.data)
|
||||
|
||||
def test_valid_multiple_digest_or_verify(self):
|
||||
# Multiple calls to digest
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
first_mac = cipher.digest()
|
||||
for x in range(4):
|
||||
self.assertEqual(first_mac, cipher.digest())
|
||||
|
||||
# Multiple calls to verify
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
for x in range(5):
|
||||
cipher.verify(first_mac)
|
||||
|
||||
def test_valid_encrypt_and_digest_decrypt_and_verify(self):
|
||||
# encrypt_and_digest
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
# decrypt_and_verify
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
pt = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(self.data, pt)
|
||||
|
||||
def test_invalid_multiple_encrypt_decrypt_without_msg_len(self):
|
||||
# Once per method, with or without assoc. data
|
||||
for method_name in "encrypt", "decrypt":
|
||||
for assoc_data_present in (True, False):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96)
|
||||
if assoc_data_present:
|
||||
cipher.update(self.data)
|
||||
method = getattr(cipher, method_name)
|
||||
method(self.data)
|
||||
self.assertRaises(TypeError, method, self.data)
|
||||
|
||||
def test_invalid_mixing_encrypt_decrypt(self):
|
||||
# Once per method, with or without assoc. data
|
||||
for method1_name, method2_name in (("encrypt", "decrypt"),
|
||||
("decrypt", "encrypt")):
|
||||
for assoc_data_present in (True, False):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM,
|
||||
nonce=self.nonce_96,
|
||||
msg_len=32)
|
||||
if assoc_data_present:
|
||||
cipher.update(self.data)
|
||||
getattr(cipher, method1_name)(self.data)
|
||||
self.assertRaises(TypeError, getattr(cipher, method2_name),
|
||||
self.data)
|
||||
|
||||
def test_invalid_encrypt_or_update_after_digest(self):
|
||||
for method_name in "encrypt", "update":
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.encrypt(self.data)
|
||||
cipher.digest()
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.encrypt_and_digest(self.data)
|
||||
|
||||
def test_invalid_decrypt_or_update_after_verify(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
for method_name in "decrypt", "update":
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96)
|
||||
cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data)
|
||||
|
||||
|
||||
class TestVectors(unittest.TestCase):
|
||||
"""Class exercising the CCM test vectors found in Appendix C
|
||||
of NIST SP 800-38C and in RFC 3610"""
|
||||
|
||||
# List of test vectors, each made up of:
|
||||
# - authenticated data
|
||||
# - plaintext
|
||||
# - ciphertext
|
||||
# - MAC
|
||||
# - AES key
|
||||
# - nonce
|
||||
test_vectors_hex = [
|
||||
# NIST SP 800 38C
|
||||
( '0001020304050607',
|
||||
'20212223',
|
||||
'7162015b',
|
||||
'4dac255d',
|
||||
'404142434445464748494a4b4c4d4e4f',
|
||||
'10111213141516'),
|
||||
( '000102030405060708090a0b0c0d0e0f',
|
||||
'202122232425262728292a2b2c2d2e2f',
|
||||
'd2a1f0e051ea5f62081a7792073d593d',
|
||||
'1fc64fbfaccd',
|
||||
'404142434445464748494a4b4c4d4e4f',
|
||||
'1011121314151617'),
|
||||
( '000102030405060708090a0b0c0d0e0f10111213',
|
||||
'202122232425262728292a2b2c2d2e2f3031323334353637',
|
||||
'e3b201a9f5b71a7a9b1ceaeccd97e70b6176aad9a4428aa5',
|
||||
'484392fbc1b09951',
|
||||
'404142434445464748494a4b4c4d4e4f',
|
||||
'101112131415161718191a1b'),
|
||||
( (''.join(["%02X" % (x*16+y) for x in range(0,16) for y in range(0,16)]))*256,
|
||||
'202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f',
|
||||
'69915dad1e84c6376a68c2967e4dab615ae0fd1faec44cc484828529463ccf72',
|
||||
'b4ac6bec93e8598e7f0dadbcea5b',
|
||||
'404142434445464748494a4b4c4d4e4f',
|
||||
'101112131415161718191a1b1c'),
|
||||
# RFC3610
|
||||
( '0001020304050607',
|
||||
'08090a0b0c0d0e0f101112131415161718191a1b1c1d1e',
|
||||
'588c979a61c663d2f066d0c2c0f989806d5f6b61dac384',
|
||||
'17e8d12cfdf926e0',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'00000003020100a0a1a2a3a4a5'),
|
||||
(
|
||||
'0001020304050607',
|
||||
'08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
|
||||
'72c91a36e135f8cf291ca894085c87e3cc15c439c9e43a3b',
|
||||
'a091d56e10400916',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'00000004030201a0a1a2a3a4a5'),
|
||||
( '0001020304050607',
|
||||
'08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20',
|
||||
'51b1e5f44a197d1da46b0f8e2d282ae871e838bb64da859657',
|
||||
'4adaa76fbd9fb0c5',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'00000005040302A0A1A2A3A4A5'),
|
||||
( '000102030405060708090a0b',
|
||||
'0c0d0e0f101112131415161718191a1b1c1d1e',
|
||||
'a28c6865939a9a79faaa5c4c2a9d4a91cdac8c',
|
||||
'96c861b9c9e61ef1',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'00000006050403a0a1a2a3a4a5'),
|
||||
( '000102030405060708090a0b',
|
||||
'0c0d0e0f101112131415161718191a1b1c1d1e1f',
|
||||
'dcf1fb7b5d9e23fb9d4e131253658ad86ebdca3e',
|
||||
'51e83f077d9c2d93',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'00000007060504a0a1a2a3a4a5'),
|
||||
( '000102030405060708090a0b',
|
||||
'0c0d0e0f101112131415161718191a1b1c1d1e1f20',
|
||||
'6fc1b011f006568b5171a42d953d469b2570a4bd87',
|
||||
'405a0443ac91cb94',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'00000008070605a0a1a2a3a4a5'),
|
||||
( '0001020304050607',
|
||||
'08090a0b0c0d0e0f101112131415161718191a1b1c1d1e',
|
||||
'0135d1b2c95f41d5d1d4fec185d166b8094e999dfed96c',
|
||||
'048c56602c97acbb7490',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'00000009080706a0a1a2a3a4a5'),
|
||||
( '0001020304050607',
|
||||
'08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
|
||||
'7b75399ac0831dd2f0bbd75879a2fd8f6cae6b6cd9b7db24',
|
||||
'c17b4433f434963f34b4',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'0000000a090807a0a1a2a3a4a5'),
|
||||
( '0001020304050607',
|
||||
'08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20',
|
||||
'82531a60cc24945a4b8279181ab5c84df21ce7f9b73f42e197',
|
||||
'ea9c07e56b5eb17e5f4e',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'0000000b0a0908a0a1a2a3a4a5'),
|
||||
( '000102030405060708090a0b',
|
||||
'0c0d0e0f101112131415161718191a1b1c1d1e',
|
||||
'07342594157785152b074098330abb141b947b',
|
||||
'566aa9406b4d999988dd',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'0000000c0b0a09a0a1a2a3a4a5'),
|
||||
( '000102030405060708090a0b',
|
||||
'0c0d0e0f101112131415161718191a1b1c1d1e1f',
|
||||
'676bb20380b0e301e8ab79590a396da78b834934',
|
||||
'f53aa2e9107a8b6c022c',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'0000000d0c0b0aa0a1a2a3a4a5'),
|
||||
( '000102030405060708090a0b',
|
||||
'0c0d0e0f101112131415161718191a1b1c1d1e1f20',
|
||||
'c0ffa0d6f05bdb67f24d43a4338d2aa4bed7b20e43',
|
||||
'cd1aa31662e7ad65d6db',
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf',
|
||||
'0000000e0d0c0ba0a1a2a3a4a5'),
|
||||
( '0be1a88bace018b1',
|
||||
'08e8cf97d820ea258460e96ad9cf5289054d895ceac47c',
|
||||
'4cb97f86a2a4689a877947ab8091ef5386a6ffbdd080f8',
|
||||
'e78cf7cb0cddd7b3',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'00412b4ea9cdbe3c9696766cfa'),
|
||||
( '63018f76dc8a1bcb',
|
||||
'9020ea6f91bdd85afa0039ba4baff9bfb79c7028949cd0ec',
|
||||
'4ccb1e7ca981befaa0726c55d378061298c85c92814abc33',
|
||||
'c52ee81d7d77c08a',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'0033568ef7b2633c9696766cfa'),
|
||||
( 'aa6cfa36cae86b40',
|
||||
'b916e0eacc1c00d7dcec68ec0b3bbb1a02de8a2d1aa346132e',
|
||||
'b1d23a2220ddc0ac900d9aa03c61fcf4a559a4417767089708',
|
||||
'a776796edb723506',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'00103fe41336713c9696766cfa'),
|
||||
( 'd0d0735c531e1becf049c244',
|
||||
'12daac5630efa5396f770ce1a66b21f7b2101c',
|
||||
'14d253c3967b70609b7cbb7c49916028324526',
|
||||
'9a6f49975bcadeaf',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'00764c63b8058e3c9696766cfa'),
|
||||
( '77b60f011c03e1525899bcae',
|
||||
'e88b6a46c78d63e52eb8c546efb5de6f75e9cc0d',
|
||||
'5545ff1a085ee2efbf52b2e04bee1e2336c73e3f',
|
||||
'762c0c7744fe7e3c',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'00f8b678094e3b3c9696766cfa'),
|
||||
( 'cd9044d2b71fdb8120ea60c0',
|
||||
'6435acbafb11a82e2f071d7ca4a5ebd93a803ba87f',
|
||||
'009769ecabdf48625594c59251e6035722675e04c8',
|
||||
'47099e5ae0704551',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'00d560912d3f703c9696766cfa'),
|
||||
( 'd85bc7e69f944fb8',
|
||||
'8a19b950bcf71a018e5e6701c91787659809d67dbedd18',
|
||||
'bc218daa947427b6db386a99ac1aef23ade0b52939cb6a',
|
||||
'637cf9bec2408897c6ba',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'0042fff8f1951c3c9696766cfa'),
|
||||
( '74a0ebc9069f5b37',
|
||||
'1761433c37c5a35fc1f39f406302eb907c6163be38c98437',
|
||||
'5810e6fd25874022e80361a478e3e9cf484ab04f447efff6',
|
||||
'f0a477cc2fc9bf548944',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'00920f40e56cdc3c9696766cfa'),
|
||||
( '44a3aa3aae6475ca',
|
||||
'a434a8e58500c6e41530538862d686ea9e81301b5ae4226bfa',
|
||||
'f2beed7bc5098e83feb5b31608f8e29c38819a89c8e776f154',
|
||||
'4d4151a4ed3a8b87b9ce',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'0027ca0c7120bc3c9696766cfa'),
|
||||
( 'ec46bb63b02520c33c49fd70',
|
||||
'b96b49e21d621741632875db7f6c9243d2d7c2',
|
||||
'31d750a09da3ed7fddd49a2032aabf17ec8ebf',
|
||||
'7d22c8088c666be5c197',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'005b8ccbcd9af83c9696766cfa'),
|
||||
( '47a65ac78b3d594227e85e71',
|
||||
'e2fcfbb880442c731bf95167c8ffd7895e337076',
|
||||
'e882f1dbd38ce3eda7c23f04dd65071eb41342ac',
|
||||
'df7e00dccec7ae52987d',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'003ebe94044b9a3c9696766cfa'),
|
||||
( '6e37a6ef546d955d34ab6059',
|
||||
'abf21c0b02feb88f856df4a37381bce3cc128517d4',
|
||||
'f32905b88a641b04b9c9ffb58cc390900f3da12ab1',
|
||||
'6dce9e82efa16da62059',
|
||||
'd7828d13b2b0bdc325a76236df93cc6b',
|
||||
'008d493b30ae8b3c9696766cfa'),
|
||||
]
|
||||
|
||||
test_vectors = [[unhexlify(x) for x in tv] for tv in test_vectors_hex]
|
||||
|
||||
def runTest(self):
|
||||
for assoc_data, pt, ct, mac, key, nonce in self.test_vectors:
|
||||
# Encrypt
|
||||
cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=len(mac))
|
||||
cipher.update(assoc_data)
|
||||
ct2, mac2 = cipher.encrypt_and_digest(pt)
|
||||
self.assertEqual(ct, ct2)
|
||||
self.assertEqual(mac, mac2)
|
||||
|
||||
# Decrypt
|
||||
cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=len(mac))
|
||||
cipher.update(assoc_data)
|
||||
pt2 = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
|
||||
class TestVectorsWycheproof(unittest.TestCase):
|
||||
|
||||
def __init__(self, wycheproof_warnings, **extra_params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self._wycheproof_warnings = wycheproof_warnings
|
||||
self._extra_params = extra_params
|
||||
self._id = "None"
|
||||
|
||||
def setUp(self):
|
||||
|
||||
def filter_tag(group):
|
||||
return group['tagSize'] // 8
|
||||
|
||||
self.tv = load_test_vectors_wycheproof(("Cipher", "wycheproof"),
|
||||
"aes_ccm_test.json",
|
||||
"Wycheproof AES CCM",
|
||||
group_tag={'tag_size': filter_tag})
|
||||
|
||||
def shortDescription(self):
|
||||
return self._id
|
||||
|
||||
def warn(self, tv):
|
||||
if tv.warning and self._wycheproof_warnings:
|
||||
import warnings
|
||||
warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment))
|
||||
|
||||
def test_encrypt(self, tv):
|
||||
self._id = "Wycheproof Encrypt CCM Test #" + str(tv.id)
|
||||
|
||||
try:
|
||||
cipher = AES.new(tv.key, AES.MODE_CCM, tv.iv, mac_len=tv.tag_size,
|
||||
**self._extra_params)
|
||||
except ValueError as e:
|
||||
if len(tv.iv) not in range(7, 13 + 1, 2) and "Length of parameter 'nonce'" in str(e):
|
||||
assert not tv.valid
|
||||
return
|
||||
if tv.tag_size not in range(4, 16 + 1, 2) and "Parameter 'mac_len'" in str(e):
|
||||
assert not tv.valid
|
||||
return
|
||||
raise e
|
||||
|
||||
cipher.update(tv.aad)
|
||||
ct, tag = cipher.encrypt_and_digest(tv.msg)
|
||||
if tv.valid:
|
||||
self.assertEqual(ct, tv.ct)
|
||||
self.assertEqual(tag, tv.tag)
|
||||
self.warn(tv)
|
||||
|
||||
def test_decrypt(self, tv):
|
||||
self._id = "Wycheproof Decrypt CCM Test #" + str(tv.id)
|
||||
|
||||
try:
|
||||
cipher = AES.new(tv.key, AES.MODE_CCM, tv.iv, mac_len=tv.tag_size,
|
||||
**self._extra_params)
|
||||
except ValueError as e:
|
||||
if len(tv.iv) not in range(7, 13 + 1, 2) and "Length of parameter 'nonce'" in str(e):
|
||||
assert not tv.valid
|
||||
return
|
||||
if tv.tag_size not in range(4, 16 + 1, 2) and "Parameter 'mac_len'" in str(e):
|
||||
assert not tv.valid
|
||||
return
|
||||
raise e
|
||||
|
||||
cipher.update(tv.aad)
|
||||
try:
|
||||
pt = cipher.decrypt_and_verify(tv.ct, tv.tag)
|
||||
except ValueError:
|
||||
assert not tv.valid
|
||||
else:
|
||||
assert tv.valid
|
||||
self.assertEqual(pt, tv.msg)
|
||||
self.warn(tv)
|
||||
|
||||
def test_corrupt_decrypt(self, tv):
|
||||
self._id = "Wycheproof Corrupt Decrypt CCM Test #" + str(tv.id)
|
||||
if len(tv.iv) not in range(7, 13 + 1, 2) or len(tv.ct) == 0:
|
||||
return
|
||||
cipher = AES.new(tv.key, AES.MODE_CCM, tv.iv, mac_len=tv.tag_size,
|
||||
**self._extra_params)
|
||||
cipher.update(tv.aad)
|
||||
ct_corrupt = strxor(tv.ct, b"\x00" * (len(tv.ct) - 1) + b"\x01")
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct_corrupt, tv.tag)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
for tv in self.tv:
|
||||
self.test_encrypt(tv)
|
||||
self.test_decrypt(tv)
|
||||
self.test_corrupt_decrypt(tv)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
wycheproof_warnings = config.get('wycheproof_warnings')
|
||||
|
||||
tests = []
|
||||
tests += list_test_cases(CcmTests)
|
||||
tests += list_test_cases(CcmFSMTests)
|
||||
tests += [TestVectors()]
|
||||
tests += [TestVectorsWycheproof(wycheproof_warnings)]
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def suite():
|
||||
unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,411 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.SelfTest.loader import load_test_vectors
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.Util.py3compat import tobytes, is_string
|
||||
from Crypto.Cipher import AES, DES3, DES
|
||||
from Crypto.Hash import SHAKE128
|
||||
|
||||
from Crypto.SelfTest.Cipher.test_CBC import BlockChainingTests
|
||||
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
|
||||
class CfbTests(BlockChainingTests):
|
||||
|
||||
aes_mode = AES.MODE_CFB
|
||||
des3_mode = DES3.MODE_CFB
|
||||
|
||||
# Redefine test_unaligned_data_128/64
|
||||
|
||||
def test_unaligned_data_128(self):
|
||||
plaintexts = [ b"7777777" ] * 100
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=8)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=8)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=128)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=128)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
def test_unaligned_data_64(self):
|
||||
plaintexts = [ b"7777777" ] * 100
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=8)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=8)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=64)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=64)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
# Extra
|
||||
|
||||
def test_segment_size_128(self):
|
||||
for bits in range(8, 129, 8):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128,
|
||||
segment_size=bits)
|
||||
|
||||
for bits in 0, 7, 9, 127, 129:
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CFB,
|
||||
self.iv_128,
|
||||
segment_size=bits)
|
||||
|
||||
def test_segment_size_64(self):
|
||||
for bits in range(8, 65, 8):
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64,
|
||||
segment_size=bits)
|
||||
|
||||
for bits in 0, 7, 9, 63, 65:
|
||||
self.assertRaises(ValueError, DES3.new, self.key_192, AES.MODE_CFB,
|
||||
self.iv_64,
|
||||
segment_size=bits)
|
||||
|
||||
|
||||
class NistCfbVectors(unittest.TestCase):
|
||||
|
||||
def _do_kat_aes_test(self, file_name, segment_size):
|
||||
|
||||
test_vectors = load_test_vectors(("Cipher", "AES"),
|
||||
file_name,
|
||||
"AES CFB%d KAT" % segment_size,
|
||||
{ "count" : lambda x: int(x) } )
|
||||
if test_vectors is None:
|
||||
return
|
||||
|
||||
direction = None
|
||||
for tv in test_vectors:
|
||||
|
||||
# The test vector file contains some directive lines
|
||||
if is_string(tv):
|
||||
direction = tv
|
||||
continue
|
||||
|
||||
self.description = tv.desc
|
||||
cipher = AES.new(tv.key, AES.MODE_CFB, tv.iv,
|
||||
segment_size=segment_size)
|
||||
if direction == "[ENCRYPT]":
|
||||
self.assertEqual(cipher.encrypt(tv.plaintext), tv.ciphertext)
|
||||
elif direction == "[DECRYPT]":
|
||||
self.assertEqual(cipher.decrypt(tv.ciphertext), tv.plaintext)
|
||||
else:
|
||||
assert False
|
||||
|
||||
# See Section 6.4.5 in AESAVS
|
||||
def _do_mct_aes_test(self, file_name, segment_size):
|
||||
|
||||
test_vectors = load_test_vectors(("Cipher", "AES"),
|
||||
file_name,
|
||||
"AES CFB%d Montecarlo" % segment_size,
|
||||
{ "count" : lambda x: int(x) } )
|
||||
if test_vectors is None:
|
||||
return
|
||||
|
||||
assert(segment_size in (8, 128))
|
||||
|
||||
direction = None
|
||||
for tv in test_vectors:
|
||||
|
||||
# The test vector file contains some directive lines
|
||||
if is_string(tv):
|
||||
direction = tv
|
||||
continue
|
||||
|
||||
self.description = tv.desc
|
||||
cipher = AES.new(tv.key, AES.MODE_CFB, tv.iv,
|
||||
segment_size=segment_size)
|
||||
|
||||
def get_input(input_text, output_seq, j):
|
||||
# CFB128
|
||||
if segment_size == 128:
|
||||
if j >= 2:
|
||||
return output_seq[-2]
|
||||
return [input_text, tv.iv][j]
|
||||
# CFB8
|
||||
if j == 0:
|
||||
return input_text
|
||||
elif j <= 16:
|
||||
return tv.iv[j - 1:j]
|
||||
return output_seq[j - 17]
|
||||
|
||||
if direction == '[ENCRYPT]':
|
||||
cts = []
|
||||
for j in range(1000):
|
||||
plaintext = get_input(tv.plaintext, cts, j)
|
||||
cts.append(cipher.encrypt(plaintext))
|
||||
self.assertEqual(cts[-1], tv.ciphertext)
|
||||
elif direction == '[DECRYPT]':
|
||||
pts = []
|
||||
for j in range(1000):
|
||||
ciphertext = get_input(tv.ciphertext, pts, j)
|
||||
pts.append(cipher.decrypt(ciphertext))
|
||||
self.assertEqual(pts[-1], tv.plaintext)
|
||||
else:
|
||||
assert False
|
||||
|
||||
def _do_tdes_test(self, file_name, segment_size):
|
||||
|
||||
test_vectors = load_test_vectors(("Cipher", "TDES"),
|
||||
file_name,
|
||||
"TDES CFB%d KAT" % segment_size,
|
||||
{ "count" : lambda x: int(x) } )
|
||||
if test_vectors is None:
|
||||
return
|
||||
|
||||
direction = None
|
||||
for tv in test_vectors:
|
||||
|
||||
# The test vector file contains some directive lines
|
||||
if is_string(tv):
|
||||
direction = tv
|
||||
continue
|
||||
|
||||
self.description = tv.desc
|
||||
if hasattr(tv, "keys"):
|
||||
cipher = DES.new(tv.keys, DES.MODE_CFB, tv.iv,
|
||||
segment_size=segment_size)
|
||||
else:
|
||||
if tv.key1 != tv.key3:
|
||||
key = tv.key1 + tv.key2 + tv.key3 # Option 3
|
||||
else:
|
||||
key = tv.key1 + tv.key2 # Option 2
|
||||
cipher = DES3.new(key, DES3.MODE_CFB, tv.iv,
|
||||
segment_size=segment_size)
|
||||
if direction == "[ENCRYPT]":
|
||||
self.assertEqual(cipher.encrypt(tv.plaintext), tv.ciphertext)
|
||||
elif direction == "[DECRYPT]":
|
||||
self.assertEqual(cipher.decrypt(tv.ciphertext), tv.plaintext)
|
||||
else:
|
||||
assert False
|
||||
|
||||
|
||||
# Create one test method per file
|
||||
nist_aes_kat_mmt_files = (
|
||||
# KAT
|
||||
"CFB?GFSbox128.rsp",
|
||||
"CFB?GFSbox192.rsp",
|
||||
"CFB?GFSbox256.rsp",
|
||||
"CFB?KeySbox128.rsp",
|
||||
"CFB?KeySbox192.rsp",
|
||||
"CFB?KeySbox256.rsp",
|
||||
"CFB?VarKey128.rsp",
|
||||
"CFB?VarKey192.rsp",
|
||||
"CFB?VarKey256.rsp",
|
||||
"CFB?VarTxt128.rsp",
|
||||
"CFB?VarTxt192.rsp",
|
||||
"CFB?VarTxt256.rsp",
|
||||
# MMT
|
||||
"CFB?MMT128.rsp",
|
||||
"CFB?MMT192.rsp",
|
||||
"CFB?MMT256.rsp",
|
||||
)
|
||||
nist_aes_mct_files = (
|
||||
"CFB?MCT128.rsp",
|
||||
"CFB?MCT192.rsp",
|
||||
"CFB?MCT256.rsp",
|
||||
)
|
||||
|
||||
for file_gen_name in nist_aes_kat_mmt_files:
|
||||
for bits in "8", "128":
|
||||
file_name = file_gen_name.replace("?", bits)
|
||||
def new_func(self, file_name=file_name, bits=bits):
|
||||
self._do_kat_aes_test(file_name, int(bits))
|
||||
setattr(NistCfbVectors, "test_AES_" + file_name, new_func)
|
||||
|
||||
for file_gen_name in nist_aes_mct_files:
|
||||
for bits in "8", "128":
|
||||
file_name = file_gen_name.replace("?", bits)
|
||||
def new_func(self, file_name=file_name, bits=bits):
|
||||
self._do_mct_aes_test(file_name, int(bits))
|
||||
setattr(NistCfbVectors, "test_AES_" + file_name, new_func)
|
||||
del file_name, new_func
|
||||
|
||||
nist_tdes_files = (
|
||||
"TCFB?MMT2.rsp", # 2TDES
|
||||
"TCFB?MMT3.rsp", # 3TDES
|
||||
"TCFB?invperm.rsp", # Single DES
|
||||
"TCFB?permop.rsp",
|
||||
"TCFB?subtab.rsp",
|
||||
"TCFB?varkey.rsp",
|
||||
"TCFB?vartext.rsp",
|
||||
)
|
||||
|
||||
for file_gen_name in nist_tdes_files:
|
||||
for bits in "8", "64":
|
||||
file_name = file_gen_name.replace("?", bits)
|
||||
def new_func(self, file_name=file_name, bits=bits):
|
||||
self._do_tdes_test(file_name, int(bits))
|
||||
setattr(NistCfbVectors, "test_TDES_" + file_name, new_func)
|
||||
|
||||
# END OF NIST CBC TEST VECTORS
|
||||
|
||||
|
||||
class SP800TestVectors(unittest.TestCase):
|
||||
"""Class exercising the CFB test vectors found in Section F.3
|
||||
of NIST SP 800-3A"""
|
||||
|
||||
def test_aes_128_cfb8(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172aae2d'
|
||||
ciphertext = '3b79424c9c0dd436bace9e0ed4586a4f32b9'
|
||||
key = '2b7e151628aed2a6abf7158809cf4f3c'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
def test_aes_192_cfb8(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172aae2d'
|
||||
ciphertext = 'cda2521ef0a905ca44cd057cbf0d47a0678a'
|
||||
key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
def test_aes_256_cfb8(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172aae2d'
|
||||
ciphertext = 'dc1f1a8520a64db55fcc8ac554844e889700'
|
||||
key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
def test_aes_128_cfb128(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = '3b3fd92eb72dad20333449f8e83cfb4a' +\
|
||||
'c8a64537a0b3a93fcde3cdad9f1ce58b' +\
|
||||
'26751f67a3cbb140b1808cf187a4f4df' +\
|
||||
'c04b05357c5d1c0eeac4c66f9ff7f2e6'
|
||||
key = '2b7e151628aed2a6abf7158809cf4f3c'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
def test_aes_192_cfb128(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = 'cdc80d6fddf18cab34c25909c99a4174' +\
|
||||
'67ce7f7f81173621961a2b70171d3d7a' +\
|
||||
'2e1e8a1dd59b88b1c8e60fed1efac4c9' +\
|
||||
'c05f9f9ca9834fa042ae8fba584b09ff'
|
||||
key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
def test_aes_256_cfb128(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
|
||||
ciphertext = 'dc7e84bfda79164b7ecd8486985d3860' +\
|
||||
'39ffed143b28b1c832113c6331e5407b' +\
|
||||
'df10132415e54b92a13ed0a8267ae2f9' +\
|
||||
'75a385741ab9cef82031623d55b1e471'
|
||||
key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(CfbTests)
|
||||
if config.get('slow_tests'):
|
||||
tests += list_test_cases(NistCfbVectors)
|
||||
tests += list_test_cases(SP800TestVectors)
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,472 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.Util.py3compat import tobytes, bchr
|
||||
from Crypto.Cipher import AES, DES3
|
||||
from Crypto.Hash import SHAKE128, SHA256
|
||||
from Crypto.Util import Counter
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
class CtrTests(unittest.TestCase):
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
key_192 = get_tag_random("key_192", 24)
|
||||
nonce_32 = get_tag_random("nonce_32", 4)
|
||||
nonce_64 = get_tag_random("nonce_64", 8)
|
||||
ctr_64 = Counter.new(32, prefix=nonce_32)
|
||||
ctr_128 = Counter.new(64, prefix=nonce_64)
|
||||
|
||||
def test_loopback_128(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
pt = get_tag_random("plaintext", 16 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_loopback_64(self):
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CTR, counter=self.ctr_64)
|
||||
pt = get_tag_random("plaintext", 8 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CTR, counter=self.ctr_64)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_invalid_counter_parameter(self):
|
||||
# Counter object is required for ciphers with short block size
|
||||
self.assertRaises(TypeError, DES3.new, self.key_192, AES.MODE_CTR)
|
||||
# Positional arguments are not allowed (Counter must be passed as
|
||||
# keyword)
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR, self.ctr_128)
|
||||
|
||||
def test_nonce_attribute(self):
|
||||
# Nonce attribute is the prefix passed to Counter (DES3)
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CTR, counter=self.ctr_64)
|
||||
self.assertEqual(cipher.nonce, self.nonce_32)
|
||||
|
||||
# Nonce attribute is the prefix passed to Counter (AES)
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
self.assertEqual(cipher.nonce, self.nonce_64)
|
||||
|
||||
# Nonce attribute is not defined if suffix is used in Counter
|
||||
counter = Counter.new(64, prefix=self.nonce_32, suffix=self.nonce_32)
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter)
|
||||
self.assertFalse(hasattr(cipher, "nonce"))
|
||||
|
||||
def test_nonce_parameter(self):
|
||||
# Nonce parameter becomes nonce attribute
|
||||
cipher1 = AES.new(self.key_128, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
self.assertEqual(cipher1.nonce, self.nonce_64)
|
||||
|
||||
counter = Counter.new(64, prefix=self.nonce_64, initial_value=0)
|
||||
cipher2 = AES.new(self.key_128, AES.MODE_CTR, counter=counter)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
pt = get_tag_random("plaintext", 65536)
|
||||
self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt))
|
||||
|
||||
# Nonce is implicitly created (for AES) when no parameters are passed
|
||||
nonce1 = AES.new(self.key_128, AES.MODE_CTR).nonce
|
||||
nonce2 = AES.new(self.key_128, AES.MODE_CTR).nonce
|
||||
self.assertNotEqual(nonce1, nonce2)
|
||||
self.assertEqual(len(nonce1), 8)
|
||||
|
||||
# Nonce can be zero-length
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, nonce=b"")
|
||||
self.assertEqual(b"", cipher.nonce)
|
||||
cipher.encrypt(b'0'*300)
|
||||
|
||||
# Nonce and Counter are mutually exclusive
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR,
|
||||
counter=self.ctr_128, nonce=self.nonce_64)
|
||||
|
||||
def test_initial_value_parameter(self):
|
||||
# Test with nonce parameter
|
||||
cipher1 = AES.new(self.key_128, AES.MODE_CTR,
|
||||
nonce=self.nonce_64, initial_value=0xFFFF)
|
||||
counter = Counter.new(64, prefix=self.nonce_64, initial_value=0xFFFF)
|
||||
cipher2 = AES.new(self.key_128, AES.MODE_CTR, counter=counter)
|
||||
pt = get_tag_random("plaintext", 65536)
|
||||
self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt))
|
||||
|
||||
# Test without nonce parameter
|
||||
cipher1 = AES.new(self.key_128, AES.MODE_CTR,
|
||||
initial_value=0xFFFF)
|
||||
counter = Counter.new(64, prefix=cipher1.nonce, initial_value=0xFFFF)
|
||||
cipher2 = AES.new(self.key_128, AES.MODE_CTR, counter=counter)
|
||||
pt = get_tag_random("plaintext", 65536)
|
||||
self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt))
|
||||
|
||||
# Initial_value and Counter are mutually exclusive
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR,
|
||||
counter=self.ctr_128, initial_value=0)
|
||||
|
||||
def test_initial_value_bytes_parameter(self):
|
||||
# Same result as when passing an integer
|
||||
cipher1 = AES.new(self.key_128, AES.MODE_CTR,
|
||||
nonce=self.nonce_64,
|
||||
initial_value=b"\x00"*6+b"\xFF\xFF")
|
||||
cipher2 = AES.new(self.key_128, AES.MODE_CTR,
|
||||
nonce=self.nonce_64, initial_value=0xFFFF)
|
||||
pt = get_tag_random("plaintext", 65536)
|
||||
self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt))
|
||||
|
||||
# Fail if the iv is too large
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR,
|
||||
initial_value=b"5"*17)
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR,
|
||||
nonce=self.nonce_64, initial_value=b"5"*9)
|
||||
|
||||
# Fail if the iv is too short
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR,
|
||||
initial_value=b"5"*15)
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR,
|
||||
nonce=self.nonce_64, initial_value=b"5"*7)
|
||||
|
||||
def test_iv_with_matching_length(self):
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR,
|
||||
counter=Counter.new(120))
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR,
|
||||
counter=Counter.new(136))
|
||||
|
||||
def test_block_size_128(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
self.assertEqual(cipher.block_size, AES.block_size)
|
||||
|
||||
def test_block_size_64(self):
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CTR, counter=self.ctr_64)
|
||||
self.assertEqual(cipher.block_size, DES3.block_size)
|
||||
|
||||
def test_unaligned_data_128(self):
|
||||
plaintexts = [ b"7777777" ] * 100
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
def test_unaligned_data_64(self):
|
||||
plaintexts = [ b"7777777" ] * 100
|
||||
cipher = DES3.new(self.key_192, AES.MODE_CTR, counter=self.ctr_64)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = DES3.new(self.key_192, AES.MODE_CTR, counter=self.ctr_64)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
cipher = DES3.new(self.key_192, AES.MODE_CTR, counter=self.ctr_64)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = DES3.new(self.key_192, AES.MODE_CTR, counter=self.ctr_64)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
def test_unknown_parameters(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR,
|
||||
7, counter=self.ctr_128)
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR,
|
||||
counter=self.ctr_128, unknown=7)
|
||||
# But some are only known by the base cipher (e.g. use_aesni consumed by the AES module)
|
||||
AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128, use_aesni=False)
|
||||
|
||||
def test_null_encryption_decryption(self):
|
||||
for func in "encrypt", "decrypt":
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
result = getattr(cipher, func)(b"")
|
||||
self.assertEqual(result, b"")
|
||||
|
||||
def test_either_encrypt_or_decrypt(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
cipher.encrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.decrypt, b"")
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
|
||||
cipher.decrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.encrypt, b"")
|
||||
|
||||
def test_wrap_around(self):
|
||||
# Counter is only 8 bits, so we can only encrypt/decrypt 256 blocks (=4096 bytes)
|
||||
counter = Counter.new(8, prefix=bchr(9) * 15)
|
||||
max_bytes = 4096
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter)
|
||||
cipher.encrypt(b'9' * max_bytes)
|
||||
self.assertRaises(OverflowError, cipher.encrypt, b'9')
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter)
|
||||
self.assertRaises(OverflowError, cipher.encrypt, b'9' * (max_bytes + 1))
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter)
|
||||
cipher.decrypt(b'9' * max_bytes)
|
||||
self.assertRaises(OverflowError, cipher.decrypt, b'9')
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter)
|
||||
self.assertRaises(OverflowError, cipher.decrypt, b'9' * (max_bytes + 1))
|
||||
|
||||
def test_bytearray(self):
|
||||
data = b"1" * 16
|
||||
iv = b"\x00" * 6 + b"\xFF\xFF"
|
||||
|
||||
# Encrypt
|
||||
cipher1 = AES.new(self.key_128, AES.MODE_CTR,
|
||||
nonce=self.nonce_64,
|
||||
initial_value=iv)
|
||||
ref1 = cipher1.encrypt(data)
|
||||
|
||||
cipher2 = AES.new(self.key_128, AES.MODE_CTR,
|
||||
nonce=bytearray(self.nonce_64),
|
||||
initial_value=bytearray(iv))
|
||||
ref2 = cipher2.encrypt(bytearray(data))
|
||||
|
||||
self.assertEqual(ref1, ref2)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
cipher3 = AES.new(self.key_128, AES.MODE_CTR,
|
||||
nonce=self.nonce_64,
|
||||
initial_value=iv)
|
||||
ref3 = cipher3.decrypt(data)
|
||||
|
||||
cipher4 = AES.new(self.key_128, AES.MODE_CTR,
|
||||
nonce=bytearray(self.nonce_64),
|
||||
initial_value=bytearray(iv))
|
||||
ref4 = cipher4.decrypt(bytearray(data))
|
||||
|
||||
self.assertEqual(ref3, ref4)
|
||||
|
||||
def test_very_long_data(self):
|
||||
cipher = AES.new(b'A' * 32, AES.MODE_CTR, nonce=b'')
|
||||
ct = cipher.encrypt(b'B' * 1000000)
|
||||
digest = SHA256.new(ct).hexdigest()
|
||||
self.assertEqual(digest, "96204fc470476561a3a8f3b6fe6d24be85c87510b638142d1d0fb90989f8a6a6")
|
||||
|
||||
def test_output_param(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = bytearray(128)
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
def test_output_param_memoryview(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = memoryview(bytearray(128))
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
def test_output_param_neg(self):
|
||||
LEN_PT = 128
|
||||
|
||||
pt = b'5' * LEN_PT
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0' * LEN_PT)
|
||||
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0' * LEN_PT)
|
||||
|
||||
shorter_output = bytearray(LEN_PT - 1)
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
class SP800TestVectors(unittest.TestCase):
|
||||
"""Class exercising the CTR test vectors found in Section F.5
|
||||
of NIST SP 800-38A"""
|
||||
|
||||
def test_aes_128(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = '874d6191b620e3261bef6864990db6ce' +\
|
||||
'9806f66b7970fdff8617187bb9fffdff' +\
|
||||
'5ae4df3edbd5d35e5b4f09020db03eab' +\
|
||||
'1e031dda2fbe03d1792170a0f3009cee'
|
||||
key = '2b7e151628aed2a6abf7158809cf4f3c'
|
||||
counter = Counter.new(nbits=16,
|
||||
prefix=unhexlify('f0f1f2f3f4f5f6f7f8f9fafbfcfd'),
|
||||
initial_value=0xfeff)
|
||||
|
||||
key = unhexlify(key)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CTR, counter=counter)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CTR, counter=counter)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
def test_aes_192(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = '1abc932417521ca24f2b0459fe7e6e0b' +\
|
||||
'090339ec0aa6faefd5ccc2c6f4ce8e94' +\
|
||||
'1e36b26bd1ebc670d1bd1d665620abf7' +\
|
||||
'4f78a7f6d29809585a97daec58c6b050'
|
||||
key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b'
|
||||
counter = Counter.new(nbits=16,
|
||||
prefix=unhexlify('f0f1f2f3f4f5f6f7f8f9fafbfcfd'),
|
||||
initial_value=0xfeff)
|
||||
|
||||
key = unhexlify(key)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CTR, counter=counter)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CTR, counter=counter)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
def test_aes_256(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = '601ec313775789a5b7a7f504bbf3d228' +\
|
||||
'f443e3ca4d62b59aca84e990cacaf5c5' +\
|
||||
'2b0930daa23de94ce87017ba2d84988d' +\
|
||||
'dfc9c58db67aada613c2dd08457941a6'
|
||||
key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4'
|
||||
counter = Counter.new(nbits=16,
|
||||
prefix=unhexlify('f0f1f2f3f4f5f6f7f8f9fafbfcfd'),
|
||||
initial_value=0xfeff)
|
||||
key = unhexlify(key)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CTR, counter=counter)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_CTR, counter=counter)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
|
||||
class RFC3686TestVectors(unittest.TestCase):
|
||||
|
||||
# Each item is a test vector with:
|
||||
# - plaintext
|
||||
# - ciphertext
|
||||
# - key (AES 128, 192 or 256 bits)
|
||||
# - counter prefix (4 byte nonce + 8 byte nonce)
|
||||
data = (
|
||||
('53696e676c6520626c6f636b206d7367',
|
||||
'e4095d4fb7a7b3792d6175a3261311b8',
|
||||
'ae6852f8121067cc4bf7a5765577f39e',
|
||||
'000000300000000000000000'),
|
||||
('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
|
||||
'5104a106168a72d9790d41ee8edad388eb2e1efc46da57c8fce630df9141be28',
|
||||
'7e24067817fae0d743d6ce1f32539163',
|
||||
'006cb6dbc0543b59da48d90b'),
|
||||
('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223',
|
||||
'c1cf48a89f2ffdd9cf4652e9efdb72d74540a42bde6d7836d59a5ceaaef3105325b2072f',
|
||||
'7691be035e5020a8ac6e618529f9a0dc',
|
||||
'00e0017b27777f3f4a1786f0'),
|
||||
('53696e676c6520626c6f636b206d7367',
|
||||
'4b55384fe259c9c84e7935a003cbe928',
|
||||
'16af5b145fc9f579c175f93e3bfb0eed863d06ccfdb78515',
|
||||
'0000004836733c147d6d93cb'),
|
||||
('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
|
||||
'453243fc609b23327edfaafa7131cd9f8490701c5ad4a79cfc1fe0ff42f4fb00',
|
||||
'7c5cb2401b3dc33c19e7340819e0f69c678c3db8e6f6a91a',
|
||||
'0096b03b020c6eadc2cb500d'),
|
||||
('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223',
|
||||
'96893fc55e5c722f540b7dd1ddf7e758d288bc95c69165884536c811662f2188abee0935',
|
||||
'02bf391ee8ecb159b959617b0965279bf59b60a786d3e0fe',
|
||||
'0007bdfd5cbd60278dcc0912'),
|
||||
('53696e676c6520626c6f636b206d7367',
|
||||
'145ad01dbf824ec7560863dc71e3e0c0',
|
||||
'776beff2851db06f4c8a0542c8696f6c6a81af1eec96b4d37fc1d689e6c1c104',
|
||||
'00000060db5672c97aa8f0b2'),
|
||||
('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
|
||||
'f05e231b3894612c49ee000b804eb2a9b8306b508f839d6a5530831d9344af1c',
|
||||
'f6d66d6bd52d59bb0796365879eff886c66dd51a5b6a99744b50590c87a23884',
|
||||
'00faac24c1585ef15a43d875'),
|
||||
('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223',
|
||||
'eb6c52821d0bbbf7ce7594462aca4faab407df866569fd07f48cc0b583d6071f1ec0e6b8',
|
||||
'ff7a617ce69148e4f1726e2f43581de2aa62d9f805532edff1eed687fb54153d',
|
||||
'001cc5b751a51d70a1c11148')
|
||||
)
|
||||
|
||||
bindata = []
|
||||
for tv in data:
|
||||
bindata.append([unhexlify(x) for x in tv])
|
||||
|
||||
def runTest(self):
|
||||
for pt, ct, key, prefix in self.bindata:
|
||||
counter = Counter.new(32, prefix=prefix)
|
||||
cipher = AES.new(key, AES.MODE_CTR, counter=counter)
|
||||
result = cipher.encrypt(pt)
|
||||
self.assertEqual(hexlify(ct), hexlify(result))
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(CtrTests)
|
||||
tests += list_test_cases(SP800TestVectors)
|
||||
tests += [ RFC3686TestVectors() ]
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,529 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import os
|
||||
import re
|
||||
import unittest
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
from Crypto.Util.py3compat import b, tobytes, bchr
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
|
||||
from Crypto.Cipher import ChaCha20
|
||||
|
||||
|
||||
class ChaCha20Test(unittest.TestCase):
|
||||
|
||||
def test_new_positive(self):
|
||||
cipher = ChaCha20.new(key=b("0")*32, nonce=b"0"*8)
|
||||
self.assertEqual(cipher.nonce, b"0" * 8)
|
||||
cipher = ChaCha20.new(key=b("0")*32, nonce=b"0"*12)
|
||||
self.assertEqual(cipher.nonce, b"0" * 12)
|
||||
|
||||
def test_new_negative(self):
|
||||
new = ChaCha20.new
|
||||
self.assertRaises(TypeError, new)
|
||||
self.assertRaises(TypeError, new, nonce=b("0"))
|
||||
self.assertRaises(ValueError, new, nonce=b("0")*8, key=b("0"))
|
||||
self.assertRaises(ValueError, new, nonce=b("0"), key=b("0")*32)
|
||||
|
||||
def test_default_nonce(self):
|
||||
cipher1 = ChaCha20.new(key=bchr(1) * 32)
|
||||
cipher2 = ChaCha20.new(key=bchr(1) * 32)
|
||||
self.assertEqual(len(cipher1.nonce), 8)
|
||||
self.assertNotEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
def test_nonce(self):
|
||||
key = b'A' * 32
|
||||
|
||||
nonce1 = b'P' * 8
|
||||
cipher1 = ChaCha20.new(key=key, nonce=nonce1)
|
||||
self.assertEqual(nonce1, cipher1.nonce)
|
||||
|
||||
nonce2 = b'Q' * 12
|
||||
cipher2 = ChaCha20.new(key=key, nonce=nonce2)
|
||||
self.assertEqual(nonce2, cipher2.nonce)
|
||||
|
||||
def test_eiter_encrypt_or_decrypt(self):
|
||||
"""Verify that a cipher cannot be used for both decrypting and encrypting"""
|
||||
|
||||
c1 = ChaCha20.new(key=b("5") * 32, nonce=b("6") * 8)
|
||||
c1.encrypt(b("8"))
|
||||
self.assertRaises(TypeError, c1.decrypt, b("9"))
|
||||
|
||||
c2 = ChaCha20.new(key=b("5") * 32, nonce=b("6") * 8)
|
||||
c2.decrypt(b("8"))
|
||||
self.assertRaises(TypeError, c2.encrypt, b("9"))
|
||||
|
||||
def test_round_trip(self):
|
||||
pt = b("A") * 1024
|
||||
c1 = ChaCha20.new(key=b("5") * 32, nonce=b("6") * 8)
|
||||
c2 = ChaCha20.new(key=b("5") * 32, nonce=b("6") * 8)
|
||||
ct = c1.encrypt(pt)
|
||||
self.assertEqual(c2.decrypt(ct), pt)
|
||||
|
||||
self.assertEqual(c1.encrypt(b("")), b(""))
|
||||
self.assertEqual(c2.decrypt(b("")), b(""))
|
||||
|
||||
def test_streaming(self):
|
||||
"""Verify that an arbitrary number of bytes can be encrypted/decrypted"""
|
||||
from Crypto.Hash import SHA1
|
||||
|
||||
segments = (1, 3, 5, 7, 11, 17, 23)
|
||||
total = sum(segments)
|
||||
|
||||
pt = b("")
|
||||
while len(pt) < total:
|
||||
pt += SHA1.new(pt).digest()
|
||||
|
||||
cipher1 = ChaCha20.new(key=b("7") * 32, nonce=b("t") * 8)
|
||||
ct = cipher1.encrypt(pt)
|
||||
|
||||
cipher2 = ChaCha20.new(key=b("7") * 32, nonce=b("t") * 8)
|
||||
cipher3 = ChaCha20.new(key=b("7") * 32, nonce=b("t") * 8)
|
||||
idx = 0
|
||||
for segment in segments:
|
||||
self.assertEqual(cipher2.decrypt(ct[idx:idx+segment]), pt[idx:idx+segment])
|
||||
self.assertEqual(cipher3.encrypt(pt[idx:idx+segment]), ct[idx:idx+segment])
|
||||
idx += segment
|
||||
|
||||
def test_seek(self):
|
||||
cipher1 = ChaCha20.new(key=b("9") * 32, nonce=b("e") * 8)
|
||||
|
||||
offset = 64 * 900 + 7
|
||||
pt = b("1") * 64
|
||||
|
||||
cipher1.encrypt(b("0") * offset)
|
||||
ct1 = cipher1.encrypt(pt)
|
||||
|
||||
cipher2 = ChaCha20.new(key=b("9") * 32, nonce=b("e") * 8)
|
||||
cipher2.seek(offset)
|
||||
ct2 = cipher2.encrypt(pt)
|
||||
|
||||
self.assertEqual(ct1, ct2)
|
||||
|
||||
def test_seek_tv(self):
|
||||
# Test Vector #4, A.1 from
|
||||
# http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04
|
||||
key = bchr(0) + bchr(255) + bchr(0) * 30
|
||||
nonce = bchr(0) * 8
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
cipher.seek(64 * 2)
|
||||
expected_key_stream = unhexlify(b(
|
||||
"72d54dfbf12ec44b362692df94137f32"
|
||||
"8fea8da73990265ec1bbbea1ae9af0ca"
|
||||
"13b25aa26cb4a648cb9b9d1be65b2c09"
|
||||
"24a66c54d545ec1b7374f4872e99f096"
|
||||
))
|
||||
ct = cipher.encrypt(bchr(0) * len(expected_key_stream))
|
||||
self.assertEqual(expected_key_stream, ct)
|
||||
|
||||
def test_rfc7539(self):
|
||||
# from https://tools.ietf.org/html/rfc7539 Annex A.1
|
||||
# Each item is: key, nonce, block #, plaintext, ciphertext
|
||||
tvs = [
|
||||
# Test Vector #1
|
||||
(
|
||||
"00"*32,
|
||||
"00"*12,
|
||||
0,
|
||||
"00"*16*4,
|
||||
"76b8e0ada0f13d90405d6ae55386bd28"
|
||||
"bdd219b8a08ded1aa836efcc8b770dc7"
|
||||
"da41597c5157488d7724e03fb8d84a37"
|
||||
"6a43b8f41518a11cc387b669b2ee6586"
|
||||
),
|
||||
# Test Vector #2
|
||||
(
|
||||
"00"*31 + "01",
|
||||
"00"*11 + "02",
|
||||
1,
|
||||
"416e79207375626d697373696f6e2074"
|
||||
"6f20746865204945544620696e74656e"
|
||||
"6465642062792074686520436f6e7472"
|
||||
"696275746f7220666f72207075626c69"
|
||||
"636174696f6e20617320616c6c206f72"
|
||||
"2070617274206f6620616e2049455446"
|
||||
"20496e7465726e65742d447261667420"
|
||||
"6f722052464320616e6420616e792073"
|
||||
"746174656d656e74206d616465207769"
|
||||
"7468696e2074686520636f6e74657874"
|
||||
"206f6620616e20494554462061637469"
|
||||
"7669747920697320636f6e7369646572"
|
||||
"656420616e20224945544620436f6e74"
|
||||
"7269627574696f6e222e205375636820"
|
||||
"73746174656d656e747320696e636c75"
|
||||
"6465206f72616c2073746174656d656e"
|
||||
"747320696e2049455446207365737369"
|
||||
"6f6e732c2061732077656c6c20617320"
|
||||
"7772697474656e20616e6420656c6563"
|
||||
"74726f6e696320636f6d6d756e696361"
|
||||
"74696f6e73206d61646520617420616e"
|
||||
"792074696d65206f7220706c6163652c"
|
||||
"20776869636820617265206164647265"
|
||||
"7373656420746f",
|
||||
"a3fbf07df3fa2fde4f376ca23e827370"
|
||||
"41605d9f4f4f57bd8cff2c1d4b7955ec"
|
||||
"2a97948bd3722915c8f3d337f7d37005"
|
||||
"0e9e96d647b7c39f56e031ca5eb6250d"
|
||||
"4042e02785ececfa4b4bb5e8ead0440e"
|
||||
"20b6e8db09d881a7c6132f420e527950"
|
||||
"42bdfa7773d8a9051447b3291ce1411c"
|
||||
"680465552aa6c405b7764d5e87bea85a"
|
||||
"d00f8449ed8f72d0d662ab052691ca66"
|
||||
"424bc86d2df80ea41f43abf937d3259d"
|
||||
"c4b2d0dfb48a6c9139ddd7f76966e928"
|
||||
"e635553ba76c5c879d7b35d49eb2e62b"
|
||||
"0871cdac638939e25e8a1e0ef9d5280f"
|
||||
"a8ca328b351c3c765989cbcf3daa8b6c"
|
||||
"cc3aaf9f3979c92b3720fc88dc95ed84"
|
||||
"a1be059c6499b9fda236e7e818b04b0b"
|
||||
"c39c1e876b193bfe5569753f88128cc0"
|
||||
"8aaa9b63d1a16f80ef2554d7189c411f"
|
||||
"5869ca52c5b83fa36ff216b9c1d30062"
|
||||
"bebcfd2dc5bce0911934fda79a86f6e6"
|
||||
"98ced759c3ff9b6477338f3da4f9cd85"
|
||||
"14ea9982ccafb341b2384dd902f3d1ab"
|
||||
"7ac61dd29c6f21ba5b862f3730e37cfd"
|
||||
"c4fd806c22f221"
|
||||
),
|
||||
# Test Vector #3
|
||||
(
|
||||
"1c9240a5eb55d38af333888604f6b5f0"
|
||||
"473917c1402b80099dca5cbc207075c0",
|
||||
"00"*11 + "02",
|
||||
42,
|
||||
"2754776173206272696c6c69672c2061"
|
||||
"6e642074686520736c6974687920746f"
|
||||
"7665730a446964206779726520616e64"
|
||||
"2067696d626c6520696e207468652077"
|
||||
"6162653a0a416c6c206d696d73792077"
|
||||
"6572652074686520626f726f676f7665"
|
||||
"732c0a416e6420746865206d6f6d6520"
|
||||
"7261746873206f757467726162652e",
|
||||
"62e6347f95ed87a45ffae7426f27a1df"
|
||||
"5fb69110044c0d73118effa95b01e5cf"
|
||||
"166d3df2d721caf9b21e5fb14c616871"
|
||||
"fd84c54f9d65b283196c7fe4f60553eb"
|
||||
"f39c6402c42234e32a356b3e764312a6"
|
||||
"1a5532055716ead6962568f87d3f3f77"
|
||||
"04c6a8d1bcd1bf4d50d6154b6da731b1"
|
||||
"87b58dfd728afa36757a797ac188d1"
|
||||
)
|
||||
]
|
||||
|
||||
for tv in tvs:
|
||||
key = unhexlify(tv[0])
|
||||
nonce = unhexlify(tv[1])
|
||||
offset = tv[2] * 64
|
||||
pt = unhexlify(tv[3])
|
||||
ct_expect = unhexlify(tv[4])
|
||||
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
if offset != 0:
|
||||
cipher.seek(offset)
|
||||
ct = cipher.encrypt(pt)
|
||||
assert(ct == ct_expect)
|
||||
|
||||
|
||||
class XChaCha20Test(unittest.TestCase):
|
||||
|
||||
# From https://tools.ietf.org/html/draft-arciszewski-xchacha-03
|
||||
|
||||
def test_hchacha20(self):
|
||||
# Section 2.2.1
|
||||
|
||||
from Crypto.Cipher.ChaCha20 import _HChaCha20
|
||||
|
||||
key = b"00:01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f"
|
||||
key = unhexlify(key.replace(b":", b""))
|
||||
|
||||
nonce = b"00:00:00:09:00:00:00:4a:00:00:00:00:31:41:59:27"
|
||||
nonce = unhexlify(nonce.replace(b":", b""))
|
||||
|
||||
subkey = _HChaCha20(key, nonce)
|
||||
|
||||
expected = b"82413b42 27b27bfe d30e4250 8a877d73 a0f9e4d5 8a74a853 c12ec413 26d3ecdc"
|
||||
expected = unhexlify(expected.replace(b" ", b""))
|
||||
|
||||
self.assertEqual(subkey, expected)
|
||||
|
||||
def test_nonce(self):
|
||||
key = b'A' * 32
|
||||
nonce = b'P' * 24
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
self.assertEqual(nonce, cipher.nonce)
|
||||
|
||||
def test_encrypt(self):
|
||||
# Section A.3.2
|
||||
|
||||
pt = b"""
|
||||
5468652064686f6c65202870726f6e6f756e6365642022646f6c652229206973
|
||||
20616c736f206b6e6f776e2061732074686520417369617469632077696c6420
|
||||
646f672c2072656420646f672c20616e642077686973746c696e6720646f672e
|
||||
2049742069732061626f7574207468652073697a65206f662061204765726d61
|
||||
6e20736865706865726420627574206c6f6f6b73206d6f7265206c696b652061
|
||||
206c6f6e672d6c656767656420666f782e205468697320686967686c7920656c
|
||||
757369766520616e6420736b696c6c6564206a756d70657220697320636c6173
|
||||
736966696564207769746820776f6c7665732c20636f796f7465732c206a6163
|
||||
6b616c732c20616e6420666f78657320696e20746865207461786f6e6f6d6963
|
||||
2066616d696c792043616e696461652e"""
|
||||
pt = unhexlify(pt.replace(b"\n", b"").replace(b" ", b""))
|
||||
|
||||
key = unhexlify(b"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")
|
||||
iv = unhexlify(b"404142434445464748494a4b4c4d4e4f5051525354555658")
|
||||
|
||||
ct = b"""
|
||||
7d0a2e6b7f7c65a236542630294e063b7ab9b555a5d5149aa21e4ae1e4fbce87
|
||||
ecc8e08a8b5e350abe622b2ffa617b202cfad72032a3037e76ffdcdc4376ee05
|
||||
3a190d7e46ca1de04144850381b9cb29f051915386b8a710b8ac4d027b8b050f
|
||||
7cba5854e028d564e453b8a968824173fc16488b8970cac828f11ae53cabd201
|
||||
12f87107df24ee6183d2274fe4c8b1485534ef2c5fbc1ec24bfc3663efaa08bc
|
||||
047d29d25043532db8391a8a3d776bf4372a6955827ccb0cdd4af403a7ce4c63
|
||||
d595c75a43e045f0cce1f29c8b93bd65afc5974922f214a40b7c402cdb91ae73
|
||||
c0b63615cdad0480680f16515a7ace9d39236464328a37743ffc28f4ddb324f4
|
||||
d0f5bbdc270c65b1749a6efff1fbaa09536175ccd29fb9e6057b307320d31683
|
||||
8a9c71f70b5b5907a66f7ea49aadc409"""
|
||||
ct = unhexlify(ct.replace(b"\n", b"").replace(b" ", b""))
|
||||
|
||||
cipher = ChaCha20.new(key=key, nonce=iv)
|
||||
cipher.seek(64) # Counter = 1
|
||||
ct_test = cipher.encrypt(pt)
|
||||
self.assertEqual(ct, ct_test)
|
||||
|
||||
|
||||
class ByteArrayTest(unittest.TestCase):
|
||||
"""Verify we can encrypt or decrypt bytearrays"""
|
||||
|
||||
def runTest(self):
|
||||
|
||||
data = b"0123"
|
||||
key = b"9" * 32
|
||||
nonce = b"t" * 8
|
||||
|
||||
# Encryption
|
||||
data_ba = bytearray(data)
|
||||
key_ba = bytearray(key)
|
||||
nonce_ba = bytearray(nonce)
|
||||
|
||||
cipher1 = ChaCha20.new(key=key, nonce=nonce)
|
||||
ct = cipher1.encrypt(data)
|
||||
|
||||
cipher2 = ChaCha20.new(key=key_ba, nonce=nonce_ba)
|
||||
key_ba[:1] = b'\xFF'
|
||||
nonce_ba[:1] = b'\xFF'
|
||||
ct_test = cipher2.encrypt(data_ba)
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decryption
|
||||
key_ba = bytearray(key)
|
||||
nonce_ba = bytearray(nonce)
|
||||
ct_ba = bytearray(ct)
|
||||
|
||||
cipher3 = ChaCha20.new(key=key_ba, nonce=nonce_ba)
|
||||
key_ba[:1] = b'\xFF'
|
||||
nonce_ba[:1] = b'\xFF'
|
||||
pt_test = cipher3.decrypt(ct_ba)
|
||||
|
||||
self.assertEqual(data, pt_test)
|
||||
|
||||
|
||||
class MemoryviewTest(unittest.TestCase):
|
||||
"""Verify we can encrypt or decrypt bytearrays"""
|
||||
|
||||
def runTest(self):
|
||||
|
||||
data = b"0123"
|
||||
key = b"9" * 32
|
||||
nonce = b"t" * 8
|
||||
|
||||
# Encryption
|
||||
data_mv = memoryview(bytearray(data))
|
||||
key_mv = memoryview(bytearray(key))
|
||||
nonce_mv = memoryview(bytearray(nonce))
|
||||
|
||||
cipher1 = ChaCha20.new(key=key, nonce=nonce)
|
||||
ct = cipher1.encrypt(data)
|
||||
|
||||
cipher2 = ChaCha20.new(key=key_mv, nonce=nonce_mv)
|
||||
key_mv[:1] = b'\xFF'
|
||||
nonce_mv[:1] = b'\xFF'
|
||||
ct_test = cipher2.encrypt(data_mv)
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decryption
|
||||
key_mv = memoryview(bytearray(key))
|
||||
nonce_mv = memoryview(bytearray(nonce))
|
||||
ct_mv = memoryview(bytearray(ct))
|
||||
|
||||
cipher3 = ChaCha20.new(key=key_mv, nonce=nonce_mv)
|
||||
key_mv[:1] = b'\xFF'
|
||||
nonce_mv[:1] = b'\xFF'
|
||||
pt_test = cipher3.decrypt(ct_mv)
|
||||
|
||||
self.assertEqual(data, pt_test)
|
||||
|
||||
|
||||
class ChaCha20_AGL_NIR(unittest.TestCase):
|
||||
|
||||
# From http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
|
||||
# and http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04
|
||||
tv = [
|
||||
( "00" * 32,
|
||||
"00" * 8,
|
||||
"76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc"
|
||||
"8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11c"
|
||||
"c387b669b2ee6586"
|
||||
"9f07e7be5551387a98ba977c732d080d"
|
||||
"cb0f29a048e3656912c6533e32ee7aed"
|
||||
"29b721769ce64e43d57133b074d839d5"
|
||||
"31ed1f28510afb45ace10a1f4b794d6f"
|
||||
),
|
||||
( "00" * 31 + "01",
|
||||
"00" * 8,
|
||||
"4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952"
|
||||
"ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea81"
|
||||
"7e9ad275ae546963"
|
||||
"3aeb5224ecf849929b9d828db1ced4dd"
|
||||
"832025e8018b8160b82284f3c949aa5a"
|
||||
"8eca00bbb4a73bdad192b5c42f73f2fd"
|
||||
"4e273644c8b36125a64addeb006c13a0"
|
||||
),
|
||||
( "00" * 32,
|
||||
"00" * 7 + "01",
|
||||
"de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df1"
|
||||
"37821031e85a050278a7084527214f73efc7fa5b5277062eb7a0433e"
|
||||
"445f41e3"
|
||||
),
|
||||
( "00" * 32,
|
||||
"01" + "00" * 7,
|
||||
"ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd1"
|
||||
"38e50d32111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d"
|
||||
"6bbdb0041b2f586b"
|
||||
),
|
||||
( "000102030405060708090a0b0c0d0e0f101112131415161718191a1b"
|
||||
"1c1d1e1f",
|
||||
"0001020304050607",
|
||||
"f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56"
|
||||
"f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f1"
|
||||
"5916155c2be8241a38008b9a26bc35941e2444177c8ade6689de9526"
|
||||
"4986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e"
|
||||
"09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a4750"
|
||||
"32b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c5"
|
||||
"07b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f7"
|
||||
"6dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2"
|
||||
"ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab7"
|
||||
"8fab78c9"
|
||||
),
|
||||
( "00" * 32,
|
||||
"00" * 7 + "02",
|
||||
"c2c64d378cd536374ae204b9ef933fcd"
|
||||
"1a8b2288b3dfa49672ab765b54ee27c7"
|
||||
"8a970e0e955c14f3a88e741b97c286f7"
|
||||
"5f8fc299e8148362fa198a39531bed6d"
|
||||
),
|
||||
]
|
||||
|
||||
def runTest(self):
|
||||
for (key, nonce, stream) in self.tv:
|
||||
c = ChaCha20.new(key=unhexlify(b(key)), nonce=unhexlify(b(nonce)))
|
||||
ct = unhexlify(b(stream))
|
||||
pt = b("\x00") * len(ct)
|
||||
self.assertEqual(c.encrypt(pt), ct)
|
||||
|
||||
|
||||
class TestOutput(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
# Encrypt/Decrypt data and test output parameter
|
||||
|
||||
key = b'4' * 32
|
||||
nonce = b'5' * 8
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
|
||||
pt = b'5' * 300
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = bytearray(len(pt))
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
output = memoryview(bytearray(len(pt)))
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*len(pt))
|
||||
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*len(pt))
|
||||
|
||||
shorter_output = bytearray(len(pt) - 1)
|
||||
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
|
||||
cipher = ChaCha20.new(key=key, nonce=nonce)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(ChaCha20Test)
|
||||
tests += list_test_cases(XChaCha20Test)
|
||||
tests.append(ChaCha20_AGL_NIR())
|
||||
tests.append(ByteArrayTest())
|
||||
tests.append(MemoryviewTest())
|
||||
tests.append(TestOutput())
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,776 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2018, Helder Eijs <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.SelfTest.loader import load_test_vectors_wycheproof
|
||||
from Crypto.Util.py3compat import tobytes
|
||||
from Crypto.Cipher import ChaCha20_Poly1305
|
||||
from Crypto.Hash import SHAKE128
|
||||
|
||||
from Crypto.Util.strxor import strxor
|
||||
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
|
||||
class ChaCha20Poly1305Tests(unittest.TestCase):
|
||||
|
||||
key_256 = get_tag_random("key_256", 32)
|
||||
nonce_96 = get_tag_random("nonce_96", 12)
|
||||
data_128 = get_tag_random("data_128", 16)
|
||||
|
||||
def test_loopback(self):
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
pt = get_tag_random("plaintext", 16 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_nonce(self):
|
||||
# Nonce can only be 8 or 12 bytes
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=b'H' * 8)
|
||||
self.assertEqual(len(cipher.nonce), 8)
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=b'H' * 12)
|
||||
self.assertEqual(len(cipher.nonce), 12)
|
||||
|
||||
# If not passed, the nonce is created randomly
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256)
|
||||
nonce1 = cipher.nonce
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256)
|
||||
nonce2 = cipher.nonce
|
||||
self.assertEqual(len(nonce1), 12)
|
||||
self.assertNotEqual(nonce1, nonce2)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data_128)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
self.assertEqual(ct, cipher.encrypt(self.data_128))
|
||||
|
||||
def test_nonce_must_be_bytes(self):
|
||||
self.assertRaises(TypeError,
|
||||
ChaCha20_Poly1305.new,
|
||||
key=self.key_256,
|
||||
nonce=u'test12345678')
|
||||
|
||||
def test_nonce_length(self):
|
||||
# nonce can only be 8 or 12 bytes long
|
||||
self.assertRaises(ValueError,
|
||||
ChaCha20_Poly1305.new,
|
||||
key=self.key_256,
|
||||
nonce=b'0' * 7)
|
||||
self.assertRaises(ValueError,
|
||||
ChaCha20_Poly1305.new,
|
||||
key=self.key_256,
|
||||
nonce=b'')
|
||||
|
||||
def test_block_size(self):
|
||||
# Not based on block ciphers
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
self.assertFalse(hasattr(cipher, 'block_size'))
|
||||
|
||||
def test_nonce_attribute(self):
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.nonce, self.nonce_96)
|
||||
|
||||
# By default, a 12 bytes long nonce is randomly generated
|
||||
nonce1 = ChaCha20_Poly1305.new(key=self.key_256).nonce
|
||||
nonce2 = ChaCha20_Poly1305.new(key=self.key_256).nonce
|
||||
self.assertEqual(len(nonce1), 12)
|
||||
self.assertNotEqual(nonce1, nonce2)
|
||||
|
||||
def test_unknown_parameters(self):
|
||||
self.assertRaises(TypeError,
|
||||
ChaCha20_Poly1305.new,
|
||||
key=self.key_256,
|
||||
param=9)
|
||||
|
||||
def test_null_encryption_decryption(self):
|
||||
for func in "encrypt", "decrypt":
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
result = getattr(cipher, func)(b"")
|
||||
self.assertEqual(result, b"")
|
||||
|
||||
def test_either_encrypt_or_decrypt(self):
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.encrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.decrypt, b"")
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.decrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.encrypt, b"")
|
||||
|
||||
def test_data_must_be_bytes(self):
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*')
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*')
|
||||
|
||||
def test_mac_len(self):
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
_, mac = cipher.encrypt_and_digest(self.data_128)
|
||||
self.assertEqual(len(mac), 16)
|
||||
|
||||
def test_invalid_mac(self):
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data_128)
|
||||
|
||||
invalid_mac = strxor_c(mac, 0x01)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct,
|
||||
invalid_mac)
|
||||
|
||||
def test_hex_mac(self):
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
mac_hex = cipher.hexdigest()
|
||||
self.assertEqual(cipher.digest(), unhexlify(mac_hex))
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.hexverify(mac_hex)
|
||||
|
||||
def test_message_chunks(self):
|
||||
# Validate that both associated data and plaintext/ciphertext
|
||||
# can be broken up in chunks of arbitrary length
|
||||
|
||||
auth_data = get_tag_random("authenticated data", 127)
|
||||
plaintext = get_tag_random("plaintext", 127)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(auth_data)
|
||||
ciphertext, ref_mac = cipher.encrypt_and_digest(plaintext)
|
||||
|
||||
def break_up(data, chunk_length):
|
||||
return [data[i:i+chunk_length] for i in range(0, len(data),
|
||||
chunk_length)]
|
||||
|
||||
# Encryption
|
||||
for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128:
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
|
||||
for chunk in break_up(auth_data, chunk_length):
|
||||
cipher.update(chunk)
|
||||
pt2 = b""
|
||||
for chunk in break_up(ciphertext, chunk_length):
|
||||
pt2 += cipher.decrypt(chunk)
|
||||
self.assertEqual(plaintext, pt2)
|
||||
cipher.verify(ref_mac)
|
||||
|
||||
# Decryption
|
||||
for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128:
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
|
||||
for chunk in break_up(auth_data, chunk_length):
|
||||
cipher.update(chunk)
|
||||
ct2 = b""
|
||||
for chunk in break_up(plaintext, chunk_length):
|
||||
ct2 += cipher.encrypt(chunk)
|
||||
self.assertEqual(ciphertext, ct2)
|
||||
self.assertEqual(cipher.digest(), ref_mac)
|
||||
|
||||
def test_bytearray(self):
|
||||
|
||||
# Encrypt
|
||||
key_ba = bytearray(self.key_256)
|
||||
nonce_ba = bytearray(self.nonce_96)
|
||||
header_ba = bytearray(self.data_128)
|
||||
data_ba = bytearray(self.data_128)
|
||||
|
||||
cipher1 = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data_128)
|
||||
ct = cipher1.encrypt(self.data_128)
|
||||
tag = cipher1.digest()
|
||||
|
||||
cipher2 = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
key_ba[:3] = b'\xFF\xFF\xFF'
|
||||
nonce_ba[:3] = b'\xFF\xFF\xFF'
|
||||
cipher2.update(header_ba)
|
||||
header_ba[:3] = b'\xFF\xFF\xFF'
|
||||
ct_test = cipher2.encrypt(data_ba)
|
||||
data_ba[:3] = b'\x99\x99\x99'
|
||||
tag_test = cipher2.digest()
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key_ba = bytearray(self.key_256)
|
||||
nonce_ba = bytearray(self.nonce_96)
|
||||
header_ba = bytearray(self.data_128)
|
||||
ct_ba = bytearray(ct)
|
||||
tag_ba = bytearray(tag)
|
||||
del data_ba
|
||||
|
||||
cipher3 = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
key_ba[:3] = b'\xFF\xFF\xFF'
|
||||
nonce_ba[:3] = b'\xFF\xFF\xFF'
|
||||
cipher3.update(header_ba)
|
||||
header_ba[:3] = b'\xFF\xFF\xFF'
|
||||
pt_test = cipher3.decrypt(ct_ba)
|
||||
ct_ba[:3] = b'\xFF\xFF\xFF'
|
||||
cipher3.verify(tag_ba)
|
||||
|
||||
self.assertEqual(pt_test, self.data_128)
|
||||
|
||||
def test_memoryview(self):
|
||||
|
||||
# Encrypt
|
||||
key_mv = memoryview(bytearray(self.key_256))
|
||||
nonce_mv = memoryview(bytearray(self.nonce_96))
|
||||
header_mv = memoryview(bytearray(self.data_128))
|
||||
data_mv = memoryview(bytearray(self.data_128))
|
||||
|
||||
cipher1 = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data_128)
|
||||
ct = cipher1.encrypt(self.data_128)
|
||||
tag = cipher1.digest()
|
||||
|
||||
cipher2 = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
key_mv[:3] = b'\xFF\xFF\xFF'
|
||||
nonce_mv[:3] = b'\xFF\xFF\xFF'
|
||||
cipher2.update(header_mv)
|
||||
header_mv[:3] = b'\xFF\xFF\xFF'
|
||||
ct_test = cipher2.encrypt(data_mv)
|
||||
data_mv[:3] = b'\x99\x99\x99'
|
||||
tag_test = cipher2.digest()
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key_mv = memoryview(bytearray(self.key_256))
|
||||
nonce_mv = memoryview(bytearray(self.nonce_96))
|
||||
header_mv = memoryview(bytearray(self.data_128))
|
||||
ct_mv = memoryview(bytearray(ct))
|
||||
tag_mv = memoryview(bytearray(tag))
|
||||
del data_mv
|
||||
|
||||
cipher3 = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
key_mv[:3] = b'\xFF\xFF\xFF'
|
||||
nonce_mv[:3] = b'\xFF\xFF\xFF'
|
||||
cipher3.update(header_mv)
|
||||
header_mv[:3] = b'\xFF\xFF\xFF'
|
||||
pt_test = cipher3.decrypt(ct_mv)
|
||||
ct_mv[:3] = b'\x99\x99\x99'
|
||||
cipher3.verify(tag_mv)
|
||||
|
||||
self.assertEqual(pt_test, self.data_128)
|
||||
|
||||
|
||||
class XChaCha20Poly1305Tests(unittest.TestCase):
|
||||
|
||||
def test_nonce(self):
|
||||
# Nonce can only be 24 bytes
|
||||
cipher = ChaCha20_Poly1305.new(key=b'Y' * 32,
|
||||
nonce=b'H' * 24)
|
||||
self.assertEqual(len(cipher.nonce), 24)
|
||||
self.assertEqual(cipher.nonce, b'H' * 24)
|
||||
|
||||
def test_encrypt(self):
|
||||
# From https://tools.ietf.org/html/draft-arciszewski-xchacha-03
|
||||
# Section A.3.1
|
||||
|
||||
pt = b"""
|
||||
4c616469657320616e642047656e746c656d656e206f662074686520636c6173
|
||||
73206f66202739393a204966204920636f756c64206f6666657220796f75206f
|
||||
6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73
|
||||
637265656e20776f756c642062652069742e"""
|
||||
pt = unhexlify(pt.replace(b"\n", b"").replace(b" ", b""))
|
||||
|
||||
aad = unhexlify(b"50515253c0c1c2c3c4c5c6c7")
|
||||
key = unhexlify(b"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")
|
||||
iv = unhexlify(b"404142434445464748494a4b4c4d4e4f5051525354555657")
|
||||
|
||||
ct = b"""
|
||||
bd6d179d3e83d43b9576579493c0e939572a1700252bfaccbed2902c21396cbb
|
||||
731c7f1b0b4aa6440bf3a82f4eda7e39ae64c6708c54c216cb96b72e1213b452
|
||||
2f8c9ba40db5d945b11b69b982c1bb9e3f3fac2bc369488f76b2383565d3fff9
|
||||
21f9664c97637da9768812f615c68b13b52e"""
|
||||
ct = unhexlify(ct.replace(b"\n", b"").replace(b" ", b""))
|
||||
|
||||
tag = unhexlify(b"c0875924c1c7987947deafd8780acf49")
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=iv)
|
||||
cipher.update(aad)
|
||||
ct_test, tag_test = cipher.encrypt_and_digest(pt)
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=iv)
|
||||
cipher.update(aad)
|
||||
cipher.decrypt_and_verify(ct, tag)
|
||||
|
||||
|
||||
class ChaCha20Poly1305FSMTests(unittest.TestCase):
|
||||
|
||||
key_256 = get_tag_random("key_256", 32)
|
||||
nonce_96 = get_tag_random("nonce_96", 12)
|
||||
data_128 = get_tag_random("data_128", 16)
|
||||
|
||||
def test_valid_init_encrypt_decrypt_digest_verify(self):
|
||||
# No authenticated data, fixed plaintext
|
||||
# Verify path INIT->ENCRYPT->DIGEST
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data_128)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->DECRYPT->VERIFY
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_init_update_digest_verify(self):
|
||||
# No plaintext, fixed authenticated data
|
||||
# Verify path INIT->UPDATE->DIGEST
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->VERIFY
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_full_path(self):
|
||||
# Fixed authenticated data, fixed plaintext
|
||||
# Verify path INIT->UPDATE->ENCRYPT->DIGEST
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
ct = cipher.encrypt(self.data_128)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->DECRYPT->VERIFY
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_init_digest(self):
|
||||
# Verify path INIT->DIGEST
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.digest()
|
||||
|
||||
def test_valid_init_verify(self):
|
||||
# Verify path INIT->VERIFY
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
mac = cipher.digest()
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_multiple_encrypt_or_decrypt(self):
|
||||
for method_name in "encrypt", "decrypt":
|
||||
for auth_data in (None, b"333", self.data_128,
|
||||
self.data_128 + b"3"):
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
if auth_data is not None:
|
||||
cipher.update(auth_data)
|
||||
method = getattr(cipher, method_name)
|
||||
method(self.data_128)
|
||||
method(self.data_128)
|
||||
method(self.data_128)
|
||||
method(self.data_128)
|
||||
|
||||
def test_valid_multiple_digest_or_verify(self):
|
||||
# Multiple calls to digest
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
first_mac = cipher.digest()
|
||||
for x in range(4):
|
||||
self.assertEqual(first_mac, cipher.digest())
|
||||
|
||||
# Multiple calls to verify
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
for x in range(5):
|
||||
cipher.verify(first_mac)
|
||||
|
||||
def test_valid_encrypt_and_digest_decrypt_and_verify(self):
|
||||
# encrypt_and_digest
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data_128)
|
||||
|
||||
# decrypt_and_verify
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
pt = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(self.data_128, pt)
|
||||
|
||||
def test_invalid_mixing_encrypt_decrypt(self):
|
||||
# Once per method, with or without assoc. data
|
||||
for method1_name, method2_name in (("encrypt", "decrypt"),
|
||||
("decrypt", "encrypt")):
|
||||
for assoc_data_present in (True, False):
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
if assoc_data_present:
|
||||
cipher.update(self.data_128)
|
||||
getattr(cipher, method1_name)(self.data_128)
|
||||
self.assertRaises(TypeError, getattr(cipher, method2_name),
|
||||
self.data_128)
|
||||
|
||||
def test_invalid_encrypt_or_update_after_digest(self):
|
||||
for method_name in "encrypt", "update":
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.encrypt(self.data_128)
|
||||
cipher.digest()
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data_128)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.encrypt_and_digest(self.data_128)
|
||||
|
||||
def test_invalid_decrypt_or_update_after_verify(self):
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data_128)
|
||||
mac = cipher.digest()
|
||||
|
||||
for method_name in "decrypt", "update":
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data_128)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data_128)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=self.key_256,
|
||||
nonce=self.nonce_96)
|
||||
cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data_128)
|
||||
|
||||
|
||||
def compact(x):
|
||||
return unhexlify(x.replace(" ", "").replace(":", ""))
|
||||
|
||||
|
||||
class TestVectorsRFC(unittest.TestCase):
|
||||
"""Test cases from RFC7539"""
|
||||
|
||||
# AAD, PT, CT, MAC, KEY, NONCE
|
||||
test_vectors_hex = [
|
||||
( '50 51 52 53 c0 c1 c2 c3 c4 c5 c6 c7',
|
||||
'4c 61 64 69 65 73 20 61 6e 64 20 47 65 6e 74 6c'
|
||||
'65 6d 65 6e 20 6f 66 20 74 68 65 20 63 6c 61 73'
|
||||
'73 20 6f 66 20 27 39 39 3a 20 49 66 20 49 20 63'
|
||||
'6f 75 6c 64 20 6f 66 66 65 72 20 79 6f 75 20 6f'
|
||||
'6e 6c 79 20 6f 6e 65 20 74 69 70 20 66 6f 72 20'
|
||||
'74 68 65 20 66 75 74 75 72 65 2c 20 73 75 6e 73'
|
||||
'63 72 65 65 6e 20 77 6f 75 6c 64 20 62 65 20 69'
|
||||
'74 2e',
|
||||
'd3 1a 8d 34 64 8e 60 db 7b 86 af bc 53 ef 7e c2'
|
||||
'a4 ad ed 51 29 6e 08 fe a9 e2 b5 a7 36 ee 62 d6'
|
||||
'3d be a4 5e 8c a9 67 12 82 fa fb 69 da 92 72 8b'
|
||||
'1a 71 de 0a 9e 06 0b 29 05 d6 a5 b6 7e cd 3b 36'
|
||||
'92 dd bd 7f 2d 77 8b 8c 98 03 ae e3 28 09 1b 58'
|
||||
'fa b3 24 e4 fa d6 75 94 55 85 80 8b 48 31 d7 bc'
|
||||
'3f f4 de f0 8e 4b 7a 9d e5 76 d2 65 86 ce c6 4b'
|
||||
'61 16',
|
||||
'1a:e1:0b:59:4f:09:e2:6a:7e:90:2e:cb:d0:60:06:91',
|
||||
'80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f'
|
||||
'90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f',
|
||||
'07 00 00 00' + '40 41 42 43 44 45 46 47',
|
||||
),
|
||||
( 'f3 33 88 86 00 00 00 00 00 00 4e 91',
|
||||
'49 6e 74 65 72 6e 65 74 2d 44 72 61 66 74 73 20'
|
||||
'61 72 65 20 64 72 61 66 74 20 64 6f 63 75 6d 65'
|
||||
'6e 74 73 20 76 61 6c 69 64 20 66 6f 72 20 61 20'
|
||||
'6d 61 78 69 6d 75 6d 20 6f 66 20 73 69 78 20 6d'
|
||||
'6f 6e 74 68 73 20 61 6e 64 20 6d 61 79 20 62 65'
|
||||
'20 75 70 64 61 74 65 64 2c 20 72 65 70 6c 61 63'
|
||||
'65 64 2c 20 6f 72 20 6f 62 73 6f 6c 65 74 65 64'
|
||||
'20 62 79 20 6f 74 68 65 72 20 64 6f 63 75 6d 65'
|
||||
'6e 74 73 20 61 74 20 61 6e 79 20 74 69 6d 65 2e'
|
||||
'20 49 74 20 69 73 20 69 6e 61 70 70 72 6f 70 72'
|
||||
'69 61 74 65 20 74 6f 20 75 73 65 20 49 6e 74 65'
|
||||
'72 6e 65 74 2d 44 72 61 66 74 73 20 61 73 20 72'
|
||||
'65 66 65 72 65 6e 63 65 20 6d 61 74 65 72 69 61'
|
||||
'6c 20 6f 72 20 74 6f 20 63 69 74 65 20 74 68 65'
|
||||
'6d 20 6f 74 68 65 72 20 74 68 61 6e 20 61 73 20'
|
||||
'2f e2 80 9c 77 6f 72 6b 20 69 6e 20 70 72 6f 67'
|
||||
'72 65 73 73 2e 2f e2 80 9d',
|
||||
'64 a0 86 15 75 86 1a f4 60 f0 62 c7 9b e6 43 bd'
|
||||
'5e 80 5c fd 34 5c f3 89 f1 08 67 0a c7 6c 8c b2'
|
||||
'4c 6c fc 18 75 5d 43 ee a0 9e e9 4e 38 2d 26 b0'
|
||||
'bd b7 b7 3c 32 1b 01 00 d4 f0 3b 7f 35 58 94 cf'
|
||||
'33 2f 83 0e 71 0b 97 ce 98 c8 a8 4a bd 0b 94 81'
|
||||
'14 ad 17 6e 00 8d 33 bd 60 f9 82 b1 ff 37 c8 55'
|
||||
'97 97 a0 6e f4 f0 ef 61 c1 86 32 4e 2b 35 06 38'
|
||||
'36 06 90 7b 6a 7c 02 b0 f9 f6 15 7b 53 c8 67 e4'
|
||||
'b9 16 6c 76 7b 80 4d 46 a5 9b 52 16 cd e7 a4 e9'
|
||||
'90 40 c5 a4 04 33 22 5e e2 82 a1 b0 a0 6c 52 3e'
|
||||
'af 45 34 d7 f8 3f a1 15 5b 00 47 71 8c bc 54 6a'
|
||||
'0d 07 2b 04 b3 56 4e ea 1b 42 22 73 f5 48 27 1a'
|
||||
'0b b2 31 60 53 fa 76 99 19 55 eb d6 31 59 43 4e'
|
||||
'ce bb 4e 46 6d ae 5a 10 73 a6 72 76 27 09 7a 10'
|
||||
'49 e6 17 d9 1d 36 10 94 fa 68 f0 ff 77 98 71 30'
|
||||
'30 5b ea ba 2e da 04 df 99 7b 71 4d 6c 6f 2c 29'
|
||||
'a6 ad 5c b4 02 2b 02 70 9b',
|
||||
'ee ad 9d 67 89 0c bb 22 39 23 36 fe a1 85 1f 38',
|
||||
'1c 92 40 a5 eb 55 d3 8a f3 33 88 86 04 f6 b5 f0'
|
||||
'47 39 17 c1 40 2b 80 09 9d ca 5c bc 20 70 75 c0',
|
||||
'00 00 00 00 01 02 03 04 05 06 07 08',
|
||||
)
|
||||
]
|
||||
|
||||
test_vectors = [[unhexlify(x.replace(" ", "").replace(":", "")) for x in tv] for tv in test_vectors_hex]
|
||||
|
||||
def runTest(self):
|
||||
for assoc_data, pt, ct, mac, key, nonce in self.test_vectors:
|
||||
# Encrypt
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
cipher.update(assoc_data)
|
||||
ct2, mac2 = cipher.encrypt_and_digest(pt)
|
||||
self.assertEqual(ct, ct2)
|
||||
self.assertEqual(mac, mac2)
|
||||
|
||||
# Decrypt
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
cipher.update(assoc_data)
|
||||
pt2 = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
|
||||
class TestVectorsWycheproof(unittest.TestCase):
|
||||
|
||||
def __init__(self, wycheproof_warnings):
|
||||
unittest.TestCase.__init__(self)
|
||||
self._wycheproof_warnings = wycheproof_warnings
|
||||
self._id = "None"
|
||||
|
||||
def load_tests(self, filename):
|
||||
|
||||
def filter_tag(group):
|
||||
return group['tagSize'] // 8
|
||||
|
||||
def filter_algo(root):
|
||||
return root['algorithm']
|
||||
|
||||
result = load_test_vectors_wycheproof(("Cipher", "wycheproof"),
|
||||
filename,
|
||||
"Wycheproof ChaCha20-Poly1305",
|
||||
root_tag={'algo': filter_algo},
|
||||
group_tag={'tag_size': filter_tag})
|
||||
return result
|
||||
|
||||
def setUp(self):
|
||||
self.tv = []
|
||||
self.tv.extend(self.load_tests("chacha20_poly1305_test.json"))
|
||||
self.tv.extend(self.load_tests("xchacha20_poly1305_test.json"))
|
||||
|
||||
def shortDescription(self):
|
||||
return self._id
|
||||
|
||||
def warn(self, tv):
|
||||
if tv.warning and self._wycheproof_warnings:
|
||||
import warnings
|
||||
warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment))
|
||||
|
||||
def test_encrypt(self, tv):
|
||||
self._id = "Wycheproof Encrypt %s Test #%s" % (tv.algo, tv.id)
|
||||
|
||||
try:
|
||||
cipher = ChaCha20_Poly1305.new(key=tv.key, nonce=tv.iv)
|
||||
except ValueError as e:
|
||||
assert len(tv.iv) not in (8, 12) and "Nonce must be" in str(e)
|
||||
return
|
||||
|
||||
cipher.update(tv.aad)
|
||||
ct, tag = cipher.encrypt_and_digest(tv.msg)
|
||||
if tv.valid:
|
||||
self.assertEqual(ct, tv.ct)
|
||||
self.assertEqual(tag, tv.tag)
|
||||
self.warn(tv)
|
||||
|
||||
def test_decrypt(self, tv):
|
||||
self._id = "Wycheproof Decrypt %s Test #%s" % (tv.algo, tv.id)
|
||||
|
||||
try:
|
||||
cipher = ChaCha20_Poly1305.new(key=tv.key, nonce=tv.iv)
|
||||
except ValueError as e:
|
||||
assert len(tv.iv) not in (8, 12) and "Nonce must be" in str(e)
|
||||
return
|
||||
|
||||
cipher.update(tv.aad)
|
||||
try:
|
||||
pt = cipher.decrypt_and_verify(tv.ct, tv.tag)
|
||||
except ValueError:
|
||||
assert not tv.valid
|
||||
else:
|
||||
assert tv.valid
|
||||
self.assertEqual(pt, tv.msg)
|
||||
self.warn(tv)
|
||||
|
||||
def test_corrupt_decrypt(self, tv):
|
||||
self._id = "Wycheproof Corrupt Decrypt ChaCha20-Poly1305 Test #" + str(tv.id)
|
||||
if len(tv.iv) == 0 or len(tv.ct) < 1:
|
||||
return
|
||||
cipher = ChaCha20_Poly1305.new(key=tv.key, nonce=tv.iv)
|
||||
cipher.update(tv.aad)
|
||||
ct_corrupt = strxor(tv.ct, b"\x00" * (len(tv.ct) - 1) + b"\x01")
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct_corrupt, tv.tag)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
for tv in self.tv:
|
||||
self.test_encrypt(tv)
|
||||
self.test_decrypt(tv)
|
||||
self.test_corrupt_decrypt(tv)
|
||||
|
||||
|
||||
class TestOutput(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
# Encrypt/Decrypt data and test output parameter
|
||||
|
||||
key = b'4' * 32
|
||||
nonce = b'5' * 12
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
|
||||
pt = b'5' * 16
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = bytearray(16)
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
output = memoryview(bytearray(16))
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16)
|
||||
|
||||
shorter_output = bytearray(7)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
wycheproof_warnings = config.get('wycheproof_warnings')
|
||||
|
||||
tests = []
|
||||
tests += list_test_cases(ChaCha20Poly1305Tests)
|
||||
tests += list_test_cases(XChaCha20Poly1305Tests)
|
||||
tests += list_test_cases(ChaCha20Poly1305FSMTests)
|
||||
tests += [TestVectorsRFC()]
|
||||
tests += [TestVectorsWycheproof(wycheproof_warnings)]
|
||||
tests += [TestOutput()]
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def suite():
|
||||
unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,374 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/DES.py: Self-test for the (Single) DES cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.DES"""
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.Cipher import DES
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key, description) tuples.
|
||||
SP800_17_B1_KEY = '01' * 8
|
||||
SP800_17_B2_PT = '00' * 8
|
||||
test_data = [
|
||||
# Test vectors from Appendix A of NIST SP 800-17
|
||||
# "Modes of Operation Validation System (MOVS): Requirements and Procedures"
|
||||
# http://csrc.nist.gov/publications/nistpubs/800-17/800-17.pdf
|
||||
|
||||
# Appendix A - "Sample Round Outputs for the DES"
|
||||
('0000000000000000', '82dcbafbdeab6602', '10316e028c8f3b4a',
|
||||
"NIST SP800-17 A"),
|
||||
|
||||
# Table B.1 - Variable Plaintext Known Answer Test
|
||||
('8000000000000000', '95f8a5e5dd31d900', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #0'),
|
||||
('4000000000000000', 'dd7f121ca5015619', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #1'),
|
||||
('2000000000000000', '2e8653104f3834ea', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #2'),
|
||||
('1000000000000000', '4bd388ff6cd81d4f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #3'),
|
||||
('0800000000000000', '20b9e767b2fb1456', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #4'),
|
||||
('0400000000000000', '55579380d77138ef', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #5'),
|
||||
('0200000000000000', '6cc5defaaf04512f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #6'),
|
||||
('0100000000000000', '0d9f279ba5d87260', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #7'),
|
||||
('0080000000000000', 'd9031b0271bd5a0a', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #8'),
|
||||
('0040000000000000', '424250b37c3dd951', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #9'),
|
||||
('0020000000000000', 'b8061b7ecd9a21e5', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #10'),
|
||||
('0010000000000000', 'f15d0f286b65bd28', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #11'),
|
||||
('0008000000000000', 'add0cc8d6e5deba1', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #12'),
|
||||
('0004000000000000', 'e6d5f82752ad63d1', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #13'),
|
||||
('0002000000000000', 'ecbfe3bd3f591a5e', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #14'),
|
||||
('0001000000000000', 'f356834379d165cd', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #15'),
|
||||
('0000800000000000', '2b9f982f20037fa9', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #16'),
|
||||
('0000400000000000', '889de068a16f0be6', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #17'),
|
||||
('0000200000000000', 'e19e275d846a1298', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #18'),
|
||||
('0000100000000000', '329a8ed523d71aec', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #19'),
|
||||
('0000080000000000', 'e7fce22557d23c97', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #20'),
|
||||
('0000040000000000', '12a9f5817ff2d65d', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #21'),
|
||||
('0000020000000000', 'a484c3ad38dc9c19', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #22'),
|
||||
('0000010000000000', 'fbe00a8a1ef8ad72', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #23'),
|
||||
('0000008000000000', '750d079407521363', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #24'),
|
||||
('0000004000000000', '64feed9c724c2faf', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #25'),
|
||||
('0000002000000000', 'f02b263b328e2b60', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #26'),
|
||||
('0000001000000000', '9d64555a9a10b852', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #27'),
|
||||
('0000000800000000', 'd106ff0bed5255d7', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #28'),
|
||||
('0000000400000000', 'e1652c6b138c64a5', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #29'),
|
||||
('0000000200000000', 'e428581186ec8f46', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #30'),
|
||||
('0000000100000000', 'aeb5f5ede22d1a36', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #31'),
|
||||
('0000000080000000', 'e943d7568aec0c5c', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #32'),
|
||||
('0000000040000000', 'df98c8276f54b04b', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #33'),
|
||||
('0000000020000000', 'b160e4680f6c696f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #34'),
|
||||
('0000000010000000', 'fa0752b07d9c4ab8', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #35'),
|
||||
('0000000008000000', 'ca3a2b036dbc8502', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #36'),
|
||||
('0000000004000000', '5e0905517bb59bcf', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #37'),
|
||||
('0000000002000000', '814eeb3b91d90726', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #38'),
|
||||
('0000000001000000', '4d49db1532919c9f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #39'),
|
||||
('0000000000800000', '25eb5fc3f8cf0621', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #40'),
|
||||
('0000000000400000', 'ab6a20c0620d1c6f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #41'),
|
||||
('0000000000200000', '79e90dbc98f92cca', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #42'),
|
||||
('0000000000100000', '866ecedd8072bb0e', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #43'),
|
||||
('0000000000080000', '8b54536f2f3e64a8', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #44'),
|
||||
('0000000000040000', 'ea51d3975595b86b', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #45'),
|
||||
('0000000000020000', 'caffc6ac4542de31', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #46'),
|
||||
('0000000000010000', '8dd45a2ddf90796c', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #47'),
|
||||
('0000000000008000', '1029d55e880ec2d0', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #48'),
|
||||
('0000000000004000', '5d86cb23639dbea9', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #49'),
|
||||
('0000000000002000', '1d1ca853ae7c0c5f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #50'),
|
||||
('0000000000001000', 'ce332329248f3228', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #51'),
|
||||
('0000000000000800', '8405d1abe24fb942', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #52'),
|
||||
('0000000000000400', 'e643d78090ca4207', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #53'),
|
||||
('0000000000000200', '48221b9937748a23', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #54'),
|
||||
('0000000000000100', 'dd7c0bbd61fafd54', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #55'),
|
||||
('0000000000000080', '2fbc291a570db5c4', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #56'),
|
||||
('0000000000000040', 'e07c30d7e4e26e12', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #57'),
|
||||
('0000000000000020', '0953e2258e8e90a1', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #58'),
|
||||
('0000000000000010', '5b711bc4ceebf2ee', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #59'),
|
||||
('0000000000000008', 'cc083f1e6d9e85f6', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #60'),
|
||||
('0000000000000004', 'd2fd8867d50d2dfe', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #61'),
|
||||
('0000000000000002', '06e7ea22ce92708f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #62'),
|
||||
('0000000000000001', '166b40b44aba4bd6', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #63'),
|
||||
|
||||
# Table B.2 - Variable Key Known Answer Test
|
||||
(SP800_17_B2_PT, '95a8d72813daa94d', '8001010101010101',
|
||||
'NIST SP800-17 B.2 #0'),
|
||||
(SP800_17_B2_PT, '0eec1487dd8c26d5', '4001010101010101',
|
||||
'NIST SP800-17 B.2 #1'),
|
||||
(SP800_17_B2_PT, '7ad16ffb79c45926', '2001010101010101',
|
||||
'NIST SP800-17 B.2 #2'),
|
||||
(SP800_17_B2_PT, 'd3746294ca6a6cf3', '1001010101010101',
|
||||
'NIST SP800-17 B.2 #3'),
|
||||
(SP800_17_B2_PT, '809f5f873c1fd761', '0801010101010101',
|
||||
'NIST SP800-17 B.2 #4'),
|
||||
(SP800_17_B2_PT, 'c02faffec989d1fc', '0401010101010101',
|
||||
'NIST SP800-17 B.2 #5'),
|
||||
(SP800_17_B2_PT, '4615aa1d33e72f10', '0201010101010101',
|
||||
'NIST SP800-17 B.2 #6'),
|
||||
(SP800_17_B2_PT, '2055123350c00858', '0180010101010101',
|
||||
'NIST SP800-17 B.2 #7'),
|
||||
(SP800_17_B2_PT, 'df3b99d6577397c8', '0140010101010101',
|
||||
'NIST SP800-17 B.2 #8'),
|
||||
(SP800_17_B2_PT, '31fe17369b5288c9', '0120010101010101',
|
||||
'NIST SP800-17 B.2 #9'),
|
||||
(SP800_17_B2_PT, 'dfdd3cc64dae1642', '0110010101010101',
|
||||
'NIST SP800-17 B.2 #10'),
|
||||
(SP800_17_B2_PT, '178c83ce2b399d94', '0108010101010101',
|
||||
'NIST SP800-17 B.2 #11'),
|
||||
(SP800_17_B2_PT, '50f636324a9b7f80', '0104010101010101',
|
||||
'NIST SP800-17 B.2 #12'),
|
||||
(SP800_17_B2_PT, 'a8468ee3bc18f06d', '0102010101010101',
|
||||
'NIST SP800-17 B.2 #13'),
|
||||
(SP800_17_B2_PT, 'a2dc9e92fd3cde92', '0101800101010101',
|
||||
'NIST SP800-17 B.2 #14'),
|
||||
(SP800_17_B2_PT, 'cac09f797d031287', '0101400101010101',
|
||||
'NIST SP800-17 B.2 #15'),
|
||||
(SP800_17_B2_PT, '90ba680b22aeb525', '0101200101010101',
|
||||
'NIST SP800-17 B.2 #16'),
|
||||
(SP800_17_B2_PT, 'ce7a24f350e280b6', '0101100101010101',
|
||||
'NIST SP800-17 B.2 #17'),
|
||||
(SP800_17_B2_PT, '882bff0aa01a0b87', '0101080101010101',
|
||||
'NIST SP800-17 B.2 #18'),
|
||||
(SP800_17_B2_PT, '25610288924511c2', '0101040101010101',
|
||||
'NIST SP800-17 B.2 #19'),
|
||||
(SP800_17_B2_PT, 'c71516c29c75d170', '0101020101010101',
|
||||
'NIST SP800-17 B.2 #20'),
|
||||
(SP800_17_B2_PT, '5199c29a52c9f059', '0101018001010101',
|
||||
'NIST SP800-17 B.2 #21'),
|
||||
(SP800_17_B2_PT, 'c22f0a294a71f29f', '0101014001010101',
|
||||
'NIST SP800-17 B.2 #22'),
|
||||
(SP800_17_B2_PT, 'ee371483714c02ea', '0101012001010101',
|
||||
'NIST SP800-17 B.2 #23'),
|
||||
(SP800_17_B2_PT, 'a81fbd448f9e522f', '0101011001010101',
|
||||
'NIST SP800-17 B.2 #24'),
|
||||
(SP800_17_B2_PT, '4f644c92e192dfed', '0101010801010101',
|
||||
'NIST SP800-17 B.2 #25'),
|
||||
(SP800_17_B2_PT, '1afa9a66a6df92ae', '0101010401010101',
|
||||
'NIST SP800-17 B.2 #26'),
|
||||
(SP800_17_B2_PT, 'b3c1cc715cb879d8', '0101010201010101',
|
||||
'NIST SP800-17 B.2 #27'),
|
||||
(SP800_17_B2_PT, '19d032e64ab0bd8b', '0101010180010101',
|
||||
'NIST SP800-17 B.2 #28'),
|
||||
(SP800_17_B2_PT, '3cfaa7a7dc8720dc', '0101010140010101',
|
||||
'NIST SP800-17 B.2 #29'),
|
||||
(SP800_17_B2_PT, 'b7265f7f447ac6f3', '0101010120010101',
|
||||
'NIST SP800-17 B.2 #30'),
|
||||
(SP800_17_B2_PT, '9db73b3c0d163f54', '0101010110010101',
|
||||
'NIST SP800-17 B.2 #31'),
|
||||
(SP800_17_B2_PT, '8181b65babf4a975', '0101010108010101',
|
||||
'NIST SP800-17 B.2 #32'),
|
||||
(SP800_17_B2_PT, '93c9b64042eaa240', '0101010104010101',
|
||||
'NIST SP800-17 B.2 #33'),
|
||||
(SP800_17_B2_PT, '5570530829705592', '0101010102010101',
|
||||
'NIST SP800-17 B.2 #34'),
|
||||
(SP800_17_B2_PT, '8638809e878787a0', '0101010101800101',
|
||||
'NIST SP800-17 B.2 #35'),
|
||||
(SP800_17_B2_PT, '41b9a79af79ac208', '0101010101400101',
|
||||
'NIST SP800-17 B.2 #36'),
|
||||
(SP800_17_B2_PT, '7a9be42f2009a892', '0101010101200101',
|
||||
'NIST SP800-17 B.2 #37'),
|
||||
(SP800_17_B2_PT, '29038d56ba6d2745', '0101010101100101',
|
||||
'NIST SP800-17 B.2 #38'),
|
||||
(SP800_17_B2_PT, '5495c6abf1e5df51', '0101010101080101',
|
||||
'NIST SP800-17 B.2 #39'),
|
||||
(SP800_17_B2_PT, 'ae13dbd561488933', '0101010101040101',
|
||||
'NIST SP800-17 B.2 #40'),
|
||||
(SP800_17_B2_PT, '024d1ffa8904e389', '0101010101020101',
|
||||
'NIST SP800-17 B.2 #41'),
|
||||
(SP800_17_B2_PT, 'd1399712f99bf02e', '0101010101018001',
|
||||
'NIST SP800-17 B.2 #42'),
|
||||
(SP800_17_B2_PT, '14c1d7c1cffec79e', '0101010101014001',
|
||||
'NIST SP800-17 B.2 #43'),
|
||||
(SP800_17_B2_PT, '1de5279dae3bed6f', '0101010101012001',
|
||||
'NIST SP800-17 B.2 #44'),
|
||||
(SP800_17_B2_PT, 'e941a33f85501303', '0101010101011001',
|
||||
'NIST SP800-17 B.2 #45'),
|
||||
(SP800_17_B2_PT, 'da99dbbc9a03f379', '0101010101010801',
|
||||
'NIST SP800-17 B.2 #46'),
|
||||
(SP800_17_B2_PT, 'b7fc92f91d8e92e9', '0101010101010401',
|
||||
'NIST SP800-17 B.2 #47'),
|
||||
(SP800_17_B2_PT, 'ae8e5caa3ca04e85', '0101010101010201',
|
||||
'NIST SP800-17 B.2 #48'),
|
||||
(SP800_17_B2_PT, '9cc62df43b6eed74', '0101010101010180',
|
||||
'NIST SP800-17 B.2 #49'),
|
||||
(SP800_17_B2_PT, 'd863dbb5c59a91a0', '0101010101010140',
|
||||
'NIST SP800-17 B.2 #50'),
|
||||
(SP800_17_B2_PT, 'a1ab2190545b91d7', '0101010101010120',
|
||||
'NIST SP800-17 B.2 #51'),
|
||||
(SP800_17_B2_PT, '0875041e64c570f7', '0101010101010110',
|
||||
'NIST SP800-17 B.2 #52'),
|
||||
(SP800_17_B2_PT, '5a594528bebef1cc', '0101010101010108',
|
||||
'NIST SP800-17 B.2 #53'),
|
||||
(SP800_17_B2_PT, 'fcdb3291de21f0c0', '0101010101010104',
|
||||
'NIST SP800-17 B.2 #54'),
|
||||
(SP800_17_B2_PT, '869efd7f9f265a09', '0101010101010102',
|
||||
'NIST SP800-17 B.2 #55'),
|
||||
]
|
||||
|
||||
class RonRivestTest(unittest.TestCase):
|
||||
""" Ronald L. Rivest's DES test, see
|
||||
http://people.csail.mit.edu/rivest/Destest.txt
|
||||
ABSTRACT
|
||||
--------
|
||||
|
||||
We present a simple way to test the correctness of a DES implementation:
|
||||
Use the recurrence relation:
|
||||
|
||||
X0 = 9474B8E8C73BCA7D (hexadecimal)
|
||||
|
||||
X(i+1) = IF (i is even) THEN E(Xi,Xi) ELSE D(Xi,Xi)
|
||||
|
||||
to compute a sequence of 64-bit values: X0, X1, X2, ..., X16. Here
|
||||
E(X,K) denotes the DES encryption of X using key K, and D(X,K) denotes
|
||||
the DES decryption of X using key K. If you obtain
|
||||
|
||||
X16 = 1B1A2DDB4C642438
|
||||
|
||||
your implementation does not have any of the 36,568 possible single-fault
|
||||
errors described herein.
|
||||
"""
|
||||
def runTest(self):
|
||||
from binascii import b2a_hex
|
||||
|
||||
X = []
|
||||
X[0:] = [b'\x94\x74\xB8\xE8\xC7\x3B\xCA\x7D']
|
||||
|
||||
for i in range(16):
|
||||
c = DES.new(X[i],DES.MODE_ECB)
|
||||
if not (i&1): # (num&1) returns 1 for odd numbers
|
||||
X[i+1:] = [c.encrypt(X[i])] # even
|
||||
else:
|
||||
X[i+1:] = [c.decrypt(X[i])] # odd
|
||||
|
||||
self.assertEqual(b2a_hex(X[16]),
|
||||
b2a_hex(b'\x1B\x1A\x2D\xDB\x4C\x64\x24\x38'))
|
||||
|
||||
|
||||
class TestOutput(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
# Encrypt/Decrypt data and test output parameter
|
||||
|
||||
cipher = DES.new(b'4'*8, DES.MODE_ECB)
|
||||
|
||||
pt = b'5' * 8
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = bytearray(8)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
output = memoryview(bytearray(8))
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*8)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*8)
|
||||
|
||||
shorter_output = bytearray(7)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
from .common import make_block_tests
|
||||
tests = make_block_tests(DES, "DES", test_data)
|
||||
tests += [RonRivestTest()]
|
||||
tests += [TestOutput()]
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
@@ -0,0 +1,195 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/DES3.py: Self-test for the Triple-DES cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.DES3"""
|
||||
|
||||
import unittest
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
from Crypto.Cipher import DES3
|
||||
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
from Crypto.Util.py3compat import bchr, tostr
|
||||
from Crypto.SelfTest.loader import load_test_vectors
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key, description) tuples.
|
||||
test_data = [
|
||||
# Test vector from Appendix B of NIST SP 800-67
|
||||
# "Recommendation for the Triple Data Encryption Algorithm (TDEA) Block
|
||||
# Cipher"
|
||||
# http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf
|
||||
('54686520717566636b2062726f776e20666f78206a756d70',
|
||||
'a826fd8ce53b855fcce21c8112256fe668d5c05dd9b6b900',
|
||||
'0123456789abcdef23456789abcdef01456789abcdef0123',
|
||||
'NIST SP800-67 B.1'),
|
||||
|
||||
# This test is designed to test the DES3 API, not the correctness of the
|
||||
# output.
|
||||
('21e81b7ade88a259', '5c577d4d9b20c0f8',
|
||||
'9b397ebf81b1181e282f4bb8adbadc6b', 'Two-key 3DES'),
|
||||
]
|
||||
|
||||
# NIST CAVP test vectors
|
||||
|
||||
nist_tdes_mmt_files = ("TECBMMT2.rsp", "TECBMMT3.rsp")
|
||||
|
||||
for tdes_file in nist_tdes_mmt_files:
|
||||
|
||||
test_vectors = load_test_vectors(
|
||||
("Cipher", "TDES"),
|
||||
tdes_file,
|
||||
"TDES ECB (%s)" % tdes_file,
|
||||
{"count": lambda x: int(x)}) or []
|
||||
|
||||
for index, tv in enumerate(test_vectors):
|
||||
|
||||
# The test vector file contains some directive lines
|
||||
if isinstance(tv, str):
|
||||
continue
|
||||
|
||||
key = tv.key1 + tv.key2 + tv.key3
|
||||
test_data_item = (tostr(hexlify(tv.plaintext)),
|
||||
tostr(hexlify(tv.ciphertext)),
|
||||
tostr(hexlify(key)),
|
||||
"%s (%s)" % (tdes_file, index))
|
||||
test_data.append(test_data_item)
|
||||
|
||||
|
||||
class CheckParity(unittest.TestCase):
|
||||
|
||||
def test_parity_option2(self):
|
||||
before_2k = unhexlify("CABF326FA56734324FFCCABCDEFACABF")
|
||||
after_2k = DES3.adjust_key_parity(before_2k)
|
||||
self.assertEqual(after_2k,
|
||||
unhexlify("CBBF326EA46734324FFDCBBCDFFBCBBF"))
|
||||
|
||||
def test_parity_option3(self):
|
||||
before_3k = unhexlify("AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCC")
|
||||
after_3k = DES3.adjust_key_parity(before_3k)
|
||||
self.assertEqual(after_3k,
|
||||
unhexlify("ABABABABABABABABBABABABABABABABACDCDCDCDCDCDCDCD"))
|
||||
|
||||
def test_degradation(self):
|
||||
sub_key1 = bchr(1) * 8
|
||||
sub_key2 = bchr(255) * 8
|
||||
|
||||
# K1 == K2
|
||||
self.assertRaises(ValueError, DES3.adjust_key_parity,
|
||||
sub_key1 * 2 + sub_key2)
|
||||
|
||||
# K2 == K3
|
||||
self.assertRaises(ValueError, DES3.adjust_key_parity,
|
||||
sub_key1 + sub_key2 * 2)
|
||||
|
||||
# K1 == K2 == K3
|
||||
self.assertRaises(ValueError, DES3.adjust_key_parity,
|
||||
sub_key1 * 3)
|
||||
|
||||
# K1 == K2 (with different parity)
|
||||
self.assertRaises(ValueError, DES3.adjust_key_parity,
|
||||
sub_key1 + strxor_c(sub_key1, 1) + sub_key2)
|
||||
|
||||
|
||||
class DegenerateToDESTest(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
sub_key1 = bchr(1) * 8
|
||||
sub_key2 = bchr(255) * 8
|
||||
|
||||
# K1 == K2
|
||||
self.assertRaises(ValueError, DES3.new,
|
||||
sub_key1 * 2 + sub_key2,
|
||||
DES3.MODE_ECB)
|
||||
|
||||
# K2 == K3
|
||||
self.assertRaises(ValueError, DES3.new,
|
||||
sub_key1 + sub_key2 * 2,
|
||||
DES3.MODE_ECB)
|
||||
|
||||
# K1 == K2 == K3
|
||||
self.assertRaises(ValueError, DES3.new,
|
||||
sub_key1 * 3,
|
||||
DES3.MODE_ECB)
|
||||
|
||||
# K2 == K3 (parity is ignored)
|
||||
self.assertRaises(ValueError, DES3.new,
|
||||
sub_key1 + sub_key2 + strxor_c(sub_key2, 0x1),
|
||||
DES3.MODE_ECB)
|
||||
|
||||
|
||||
class TestOutput(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
# Encrypt/Decrypt data and test output parameter
|
||||
|
||||
cipher = DES3.new(b'4'*8 + b'G'*8 + b'T'*8, DES3.MODE_ECB)
|
||||
|
||||
pt = b'5' * 16
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = bytearray(16)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
output = memoryview(bytearray(16))
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16)
|
||||
|
||||
shorter_output = bytearray(7)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
from .common import make_block_tests
|
||||
|
||||
tests = []
|
||||
tests = make_block_tests(DES3, "DES3", test_data)
|
||||
tests.append(DegenerateToDESTest())
|
||||
tests += list_test_cases(CheckParity)
|
||||
tests += [TestOutput()]
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
|
||||
def suite():
|
||||
unittest.TestSuite(get_tests())
|
||||
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
@@ -0,0 +1,773 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.SelfTest.loader import load_test_vectors_wycheproof
|
||||
from Crypto.Util.py3compat import tobytes, bchr
|
||||
from Crypto.Cipher import AES, DES3
|
||||
from Crypto.Hash import SHAKE128
|
||||
|
||||
from Crypto.Util.strxor import strxor
|
||||
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
|
||||
class EaxTests(unittest.TestCase):
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
key_192 = get_tag_random("key_192", 16)
|
||||
nonce_96 = get_tag_random("nonce_128", 12)
|
||||
data_128 = get_tag_random("data_128", 16)
|
||||
|
||||
def test_loopback_128(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
pt = get_tag_random("plaintext", 16 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_loopback_64(self):
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_EAX, nonce=self.nonce_96)
|
||||
pt = get_tag_random("plaintext", 8 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_EAX, nonce=self.nonce_96)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_nonce(self):
|
||||
# If not passed, the nonce is created randomly
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX)
|
||||
nonce1 = cipher.nonce
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX)
|
||||
nonce2 = cipher.nonce
|
||||
self.assertEqual(len(nonce1), 16)
|
||||
self.assertNotEqual(nonce1, nonce2)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, self.nonce_96)
|
||||
ct = cipher.encrypt(self.data_128)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertEqual(ct, cipher.encrypt(self.data_128))
|
||||
|
||||
def test_nonce_must_be_bytes(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_EAX,
|
||||
nonce=u'test12345678')
|
||||
|
||||
def test_nonce_length(self):
|
||||
# nonce can be of any length (but not empty)
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_EAX,
|
||||
nonce=b"")
|
||||
|
||||
for x in range(1, 128):
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=bchr(1) * x)
|
||||
cipher.encrypt(bchr(1))
|
||||
|
||||
def test_block_size_128(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.block_size, AES.block_size)
|
||||
|
||||
def test_block_size_64(self):
|
||||
cipher = DES3.new(self.key_192, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.block_size, DES3.block_size)
|
||||
|
||||
def test_nonce_attribute(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.nonce, self.nonce_96)
|
||||
|
||||
# By default, a 16 bytes long nonce is randomly generated
|
||||
nonce1 = AES.new(self.key_128, AES.MODE_EAX).nonce
|
||||
nonce2 = AES.new(self.key_128, AES.MODE_EAX).nonce
|
||||
self.assertEqual(len(nonce1), 16)
|
||||
self.assertNotEqual(nonce1, nonce2)
|
||||
|
||||
def test_unknown_parameters(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_EAX,
|
||||
self.nonce_96, 7)
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96, unknown=7)
|
||||
|
||||
# But some are only known by the base cipher
|
||||
# (e.g. use_aesni consumed by the AES module)
|
||||
AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96,
|
||||
use_aesni=False)
|
||||
|
||||
def test_null_encryption_decryption(self):
|
||||
for func in "encrypt", "decrypt":
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
result = getattr(cipher, func)(b"")
|
||||
self.assertEqual(result, b"")
|
||||
|
||||
def test_either_encrypt_or_decrypt(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.encrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.decrypt, b"")
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.decrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.encrypt, b"")
|
||||
|
||||
def test_data_must_be_bytes(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*')
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*')
|
||||
|
||||
def test_mac_len(self):
|
||||
# Invalid MAC length
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96, mac_len=2-1)
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96, mac_len=16+1)
|
||||
|
||||
# Valid MAC length
|
||||
for mac_len in range(2, 16 + 1):
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96,
|
||||
mac_len=mac_len)
|
||||
_, mac = cipher.encrypt_and_digest(self.data_128)
|
||||
self.assertEqual(len(mac), mac_len)
|
||||
|
||||
# Default MAC length
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
_, mac = cipher.encrypt_and_digest(self.data_128)
|
||||
self.assertEqual(len(mac), 16)
|
||||
|
||||
def test_invalid_mac(self):
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data_128)
|
||||
|
||||
invalid_mac = strxor_c(mac, 0x01)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct,
|
||||
invalid_mac)
|
||||
|
||||
def test_hex_mac(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
mac_hex = cipher.hexdigest()
|
||||
self.assertEqual(cipher.digest(), unhexlify(mac_hex))
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.hexverify(mac_hex)
|
||||
|
||||
def test_message_chunks(self):
|
||||
# Validate that both associated data and plaintext/ciphertext
|
||||
# can be broken up in chunks of arbitrary length
|
||||
|
||||
auth_data = get_tag_random("authenticated data", 127)
|
||||
plaintext = get_tag_random("plaintext", 127)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.update(auth_data)
|
||||
ciphertext, ref_mac = cipher.encrypt_and_digest(plaintext)
|
||||
|
||||
def break_up(data, chunk_length):
|
||||
return [data[i:i+chunk_length] for i in range(0, len(data),
|
||||
chunk_length)]
|
||||
|
||||
# Encryption
|
||||
for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128:
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
|
||||
for chunk in break_up(auth_data, chunk_length):
|
||||
cipher.update(chunk)
|
||||
pt2 = b""
|
||||
for chunk in break_up(ciphertext, chunk_length):
|
||||
pt2 += cipher.decrypt(chunk)
|
||||
self.assertEqual(plaintext, pt2)
|
||||
cipher.verify(ref_mac)
|
||||
|
||||
# Decryption
|
||||
for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128:
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
|
||||
for chunk in break_up(auth_data, chunk_length):
|
||||
cipher.update(chunk)
|
||||
ct2 = b""
|
||||
for chunk in break_up(plaintext, chunk_length):
|
||||
ct2 += cipher.encrypt(chunk)
|
||||
self.assertEqual(ciphertext, ct2)
|
||||
self.assertEqual(cipher.digest(), ref_mac)
|
||||
|
||||
def test_bytearray(self):
|
||||
|
||||
# Encrypt
|
||||
key_ba = bytearray(self.key_128)
|
||||
nonce_ba = bytearray(self.nonce_96)
|
||||
header_ba = bytearray(self.data_128)
|
||||
data_ba = bytearray(self.data_128)
|
||||
|
||||
cipher1 = AES.new(self.key_128,
|
||||
AES.MODE_EAX,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data_128)
|
||||
ct = cipher1.encrypt(self.data_128)
|
||||
tag = cipher1.digest()
|
||||
|
||||
cipher2 = AES.new(key_ba,
|
||||
AES.MODE_EAX,
|
||||
nonce=nonce_ba)
|
||||
key_ba[:3] = b'\xFF\xFF\xFF'
|
||||
nonce_ba[:3] = b'\xFF\xFF\xFF'
|
||||
cipher2.update(header_ba)
|
||||
header_ba[:3] = b'\xFF\xFF\xFF'
|
||||
ct_test = cipher2.encrypt(data_ba)
|
||||
data_ba[:3] = b'\x99\x99\x99'
|
||||
tag_test = cipher2.digest()
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key_ba = bytearray(self.key_128)
|
||||
nonce_ba = bytearray(self.nonce_96)
|
||||
header_ba = bytearray(self.data_128)
|
||||
ct_ba = bytearray(ct)
|
||||
tag_ba = bytearray(tag)
|
||||
del data_ba
|
||||
|
||||
cipher3 = AES.new(key_ba,
|
||||
AES.MODE_EAX,
|
||||
nonce=nonce_ba)
|
||||
key_ba[:3] = b'\xFF\xFF\xFF'
|
||||
nonce_ba[:3] = b'\xFF\xFF\xFF'
|
||||
cipher3.update(header_ba)
|
||||
header_ba[:3] = b'\xFF\xFF\xFF'
|
||||
pt_test = cipher3.decrypt(ct_ba)
|
||||
ct_ba[:3] = b'\xFF\xFF\xFF'
|
||||
cipher3.verify(tag_ba)
|
||||
|
||||
self.assertEqual(pt_test, self.data_128)
|
||||
|
||||
def test_memoryview(self):
|
||||
|
||||
# Encrypt
|
||||
key_mv = memoryview(bytearray(self.key_128))
|
||||
nonce_mv = memoryview(bytearray(self.nonce_96))
|
||||
header_mv = memoryview(bytearray(self.data_128))
|
||||
data_mv = memoryview(bytearray(self.data_128))
|
||||
|
||||
cipher1 = AES.new(self.key_128,
|
||||
AES.MODE_EAX,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data_128)
|
||||
ct = cipher1.encrypt(self.data_128)
|
||||
tag = cipher1.digest()
|
||||
|
||||
cipher2 = AES.new(key_mv,
|
||||
AES.MODE_EAX,
|
||||
nonce=nonce_mv)
|
||||
key_mv[:3] = b'\xFF\xFF\xFF'
|
||||
nonce_mv[:3] = b'\xFF\xFF\xFF'
|
||||
cipher2.update(header_mv)
|
||||
header_mv[:3] = b'\xFF\xFF\xFF'
|
||||
ct_test = cipher2.encrypt(data_mv)
|
||||
data_mv[:3] = b'\x99\x99\x99'
|
||||
tag_test = cipher2.digest()
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key_mv = memoryview(bytearray(self.key_128))
|
||||
nonce_mv = memoryview(bytearray(self.nonce_96))
|
||||
header_mv = memoryview(bytearray(self.data_128))
|
||||
ct_mv = memoryview(bytearray(ct))
|
||||
tag_mv = memoryview(bytearray(tag))
|
||||
del data_mv
|
||||
|
||||
cipher3 = AES.new(key_mv,
|
||||
AES.MODE_EAX,
|
||||
nonce=nonce_mv)
|
||||
key_mv[:3] = b'\xFF\xFF\xFF'
|
||||
nonce_mv[:3] = b'\xFF\xFF\xFF'
|
||||
cipher3.update(header_mv)
|
||||
header_mv[:3] = b'\xFF\xFF\xFF'
|
||||
pt_test = cipher3.decrypt(ct_mv)
|
||||
ct_mv[:3] = b'\x99\x99\x99'
|
||||
cipher3.verify(tag_mv)
|
||||
|
||||
self.assertEqual(pt_test, self.data_128)
|
||||
|
||||
def test_output_param(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(pt)
|
||||
tag = cipher.digest()
|
||||
|
||||
output = bytearray(128)
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
res, tag_out = cipher.encrypt_and_digest(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
self.assertEqual(tag, tag_out)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
res = cipher.decrypt_and_verify(ct, tag, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
def test_output_param_memoryview(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = memoryview(bytearray(128))
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
def test_output_param_neg(self):
|
||||
LEN_PT = 16
|
||||
|
||||
pt = b'5' * LEN_PT
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0' * LEN_PT)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0' * LEN_PT)
|
||||
|
||||
shorter_output = bytearray(LEN_PT - 1)
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
class EaxFSMTests(unittest.TestCase):
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
nonce_96 = get_tag_random("nonce_128", 12)
|
||||
data_128 = get_tag_random("data_128", 16)
|
||||
|
||||
def test_valid_init_encrypt_decrypt_digest_verify(self):
|
||||
# No authenticated data, fixed plaintext
|
||||
# Verify path INIT->ENCRYPT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data_128)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->DECRYPT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_init_update_digest_verify(self):
|
||||
# No plaintext, fixed authenticated data
|
||||
# Verify path INIT->UPDATE->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_full_path(self):
|
||||
# Fixed authenticated data, fixed plaintext
|
||||
# Verify path INIT->UPDATE->ENCRYPT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
ct = cipher.encrypt(self.data_128)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->DECRYPT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_init_digest(self):
|
||||
# Verify path INIT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.digest()
|
||||
|
||||
def test_valid_init_verify(self):
|
||||
# Verify path INIT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
mac = cipher.digest()
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_multiple_encrypt_or_decrypt(self):
|
||||
for method_name in "encrypt", "decrypt":
|
||||
for auth_data in (None, b"333", self.data_128,
|
||||
self.data_128 + b"3"):
|
||||
if auth_data is None:
|
||||
assoc_len = None
|
||||
else:
|
||||
assoc_len = len(auth_data)
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96)
|
||||
if auth_data is not None:
|
||||
cipher.update(auth_data)
|
||||
method = getattr(cipher, method_name)
|
||||
method(self.data_128)
|
||||
method(self.data_128)
|
||||
method(self.data_128)
|
||||
method(self.data_128)
|
||||
|
||||
def test_valid_multiple_digest_or_verify(self):
|
||||
# Multiple calls to digest
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
first_mac = cipher.digest()
|
||||
for x in range(4):
|
||||
self.assertEqual(first_mac, cipher.digest())
|
||||
|
||||
# Multiple calls to verify
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
for x in range(5):
|
||||
cipher.verify(first_mac)
|
||||
|
||||
def test_valid_encrypt_and_digest_decrypt_and_verify(self):
|
||||
# encrypt_and_digest
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data_128)
|
||||
|
||||
# decrypt_and_verify
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.update(self.data_128)
|
||||
pt = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(self.data_128, pt)
|
||||
|
||||
def test_invalid_mixing_encrypt_decrypt(self):
|
||||
# Once per method, with or without assoc. data
|
||||
for method1_name, method2_name in (("encrypt", "decrypt"),
|
||||
("decrypt", "encrypt")):
|
||||
for assoc_data_present in (True, False):
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX,
|
||||
nonce=self.nonce_96)
|
||||
if assoc_data_present:
|
||||
cipher.update(self.data_128)
|
||||
getattr(cipher, method1_name)(self.data_128)
|
||||
self.assertRaises(TypeError, getattr(cipher, method2_name),
|
||||
self.data_128)
|
||||
|
||||
def test_invalid_encrypt_or_update_after_digest(self):
|
||||
for method_name in "encrypt", "update":
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.encrypt(self.data_128)
|
||||
cipher.digest()
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data_128)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.encrypt_and_digest(self.data_128)
|
||||
|
||||
def test_invalid_decrypt_or_update_after_verify(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data_128)
|
||||
mac = cipher.digest()
|
||||
|
||||
for method_name in "decrypt", "update":
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data_128)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96)
|
||||
cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data_128)
|
||||
|
||||
|
||||
class TestVectorsPaper(unittest.TestCase):
|
||||
"""Class exercising the EAX test vectors found in
|
||||
http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf"""
|
||||
|
||||
test_vectors_hex = [
|
||||
( '6bfb914fd07eae6b',
|
||||
'',
|
||||
'',
|
||||
'e037830e8389f27b025a2d6527e79d01',
|
||||
'233952dee4d5ed5f9b9c6d6ff80ff478',
|
||||
'62EC67F9C3A4A407FCB2A8C49031A8B3'
|
||||
),
|
||||
(
|
||||
'fa3bfd4806eb53fa',
|
||||
'f7fb',
|
||||
'19dd',
|
||||
'5c4c9331049d0bdab0277408f67967e5',
|
||||
'91945d3f4dcbee0bf45ef52255f095a4',
|
||||
'BECAF043B0A23D843194BA972C66DEBD'
|
||||
),
|
||||
( '234a3463c1264ac6',
|
||||
'1a47cb4933',
|
||||
'd851d5bae0',
|
||||
'3a59f238a23e39199dc9266626c40f80',
|
||||
'01f74ad64077f2e704c0f60ada3dd523',
|
||||
'70C3DB4F0D26368400A10ED05D2BFF5E'
|
||||
),
|
||||
(
|
||||
'33cce2eabff5a79d',
|
||||
'481c9e39b1',
|
||||
'632a9d131a',
|
||||
'd4c168a4225d8e1ff755939974a7bede',
|
||||
'd07cf6cbb7f313bdde66b727afd3c5e8',
|
||||
'8408DFFF3C1A2B1292DC199E46B7D617'
|
||||
),
|
||||
(
|
||||
'aeb96eaebe2970e9',
|
||||
'40d0c07da5e4',
|
||||
'071dfe16c675',
|
||||
'cb0677e536f73afe6a14b74ee49844dd',
|
||||
'35b6d0580005bbc12b0587124557d2c2',
|
||||
'FDB6B06676EEDC5C61D74276E1F8E816'
|
||||
),
|
||||
(
|
||||
'd4482d1ca78dce0f',
|
||||
'4de3b35c3fc039245bd1fb7d',
|
||||
'835bb4f15d743e350e728414',
|
||||
'abb8644fd6ccb86947c5e10590210a4f',
|
||||
'bd8e6e11475e60b268784c38c62feb22',
|
||||
'6EAC5C93072D8E8513F750935E46DA1B'
|
||||
),
|
||||
(
|
||||
'65d2017990d62528',
|
||||
'8b0a79306c9ce7ed99dae4f87f8dd61636',
|
||||
'02083e3979da014812f59f11d52630da30',
|
||||
'137327d10649b0aa6e1c181db617d7f2',
|
||||
'7c77d6e813bed5ac98baa417477a2e7d',
|
||||
'1A8C98DCD73D38393B2BF1569DEEFC19'
|
||||
),
|
||||
(
|
||||
'54b9f04e6a09189a',
|
||||
'1bda122bce8a8dbaf1877d962b8592dd2d56',
|
||||
'2ec47b2c4954a489afc7ba4897edcdae8cc3',
|
||||
'3b60450599bd02c96382902aef7f832a',
|
||||
'5fff20cafab119ca2fc73549e20f5b0d',
|
||||
'DDE59B97D722156D4D9AFF2BC7559826'
|
||||
),
|
||||
(
|
||||
'899a175897561d7e',
|
||||
'6cf36720872b8513f6eab1a8a44438d5ef11',
|
||||
'0de18fd0fdd91e7af19f1d8ee8733938b1e8',
|
||||
'e7f6d2231618102fdb7fe55ff1991700',
|
||||
'a4a4782bcffd3ec5e7ef6d8c34a56123',
|
||||
'B781FCF2F75FA5A8DE97A9CA48E522EC'
|
||||
),
|
||||
(
|
||||
'126735fcc320d25a',
|
||||
'ca40d7446e545ffaed3bd12a740a659ffbbb3ceab7',
|
||||
'cb8920f87a6c75cff39627b56e3ed197c552d295a7',
|
||||
'cfc46afc253b4652b1af3795b124ab6e',
|
||||
'8395fcf1e95bebd697bd010bc766aac3',
|
||||
'22E7ADD93CFC6393C57EC0B3C17D6B44'
|
||||
),
|
||||
]
|
||||
|
||||
test_vectors = [[unhexlify(x) for x in tv] for tv in test_vectors_hex]
|
||||
|
||||
def runTest(self):
|
||||
for assoc_data, pt, ct, mac, key, nonce in self.test_vectors:
|
||||
# Encrypt
|
||||
cipher = AES.new(key, AES.MODE_EAX, nonce, mac_len=len(mac))
|
||||
cipher.update(assoc_data)
|
||||
ct2, mac2 = cipher.encrypt_and_digest(pt)
|
||||
self.assertEqual(ct, ct2)
|
||||
self.assertEqual(mac, mac2)
|
||||
|
||||
# Decrypt
|
||||
cipher = AES.new(key, AES.MODE_EAX, nonce, mac_len=len(mac))
|
||||
cipher.update(assoc_data)
|
||||
pt2 = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
|
||||
class TestVectorsWycheproof(unittest.TestCase):
|
||||
|
||||
def __init__(self, wycheproof_warnings):
|
||||
unittest.TestCase.__init__(self)
|
||||
self._wycheproof_warnings = wycheproof_warnings
|
||||
self._id = "None"
|
||||
|
||||
def setUp(self):
|
||||
|
||||
def filter_tag(group):
|
||||
return group['tagSize'] // 8
|
||||
|
||||
self.tv = load_test_vectors_wycheproof(("Cipher", "wycheproof"),
|
||||
"aes_eax_test.json",
|
||||
"Wycheproof EAX",
|
||||
group_tag={'tag_size': filter_tag})
|
||||
|
||||
def shortDescription(self):
|
||||
return self._id
|
||||
|
||||
def warn(self, tv):
|
||||
if tv.warning and self._wycheproof_warnings:
|
||||
import warnings
|
||||
warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment))
|
||||
|
||||
def test_encrypt(self, tv):
|
||||
self._id = "Wycheproof Encrypt EAX Test #" + str(tv.id)
|
||||
|
||||
try:
|
||||
cipher = AES.new(tv.key, AES.MODE_EAX, tv.iv, mac_len=tv.tag_size)
|
||||
except ValueError as e:
|
||||
assert len(tv.iv) == 0 and "Nonce cannot be empty" in str(e)
|
||||
return
|
||||
|
||||
cipher.update(tv.aad)
|
||||
ct, tag = cipher.encrypt_and_digest(tv.msg)
|
||||
if tv.valid:
|
||||
self.assertEqual(ct, tv.ct)
|
||||
self.assertEqual(tag, tv.tag)
|
||||
self.warn(tv)
|
||||
|
||||
def test_decrypt(self, tv):
|
||||
self._id = "Wycheproof Decrypt EAX Test #" + str(tv.id)
|
||||
|
||||
try:
|
||||
cipher = AES.new(tv.key, AES.MODE_EAX, tv.iv, mac_len=tv.tag_size)
|
||||
except ValueError as e:
|
||||
assert len(tv.iv) == 0 and "Nonce cannot be empty" in str(e)
|
||||
return
|
||||
|
||||
cipher.update(tv.aad)
|
||||
try:
|
||||
pt = cipher.decrypt_and_verify(tv.ct, tv.tag)
|
||||
except ValueError:
|
||||
assert not tv.valid
|
||||
else:
|
||||
assert tv.valid
|
||||
self.assertEqual(pt, tv.msg)
|
||||
self.warn(tv)
|
||||
|
||||
def test_corrupt_decrypt(self, tv):
|
||||
self._id = "Wycheproof Corrupt Decrypt EAX Test #" + str(tv.id)
|
||||
if len(tv.iv) == 0 or len(tv.ct) < 1:
|
||||
return
|
||||
cipher = AES.new(tv.key, AES.MODE_EAX, tv.iv, mac_len=tv.tag_size)
|
||||
cipher.update(tv.aad)
|
||||
ct_corrupt = strxor(tv.ct, b"\x00" * (len(tv.ct) - 1) + b"\x01")
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct_corrupt, tv.tag)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
for tv in self.tv:
|
||||
self.test_encrypt(tv)
|
||||
self.test_decrypt(tv)
|
||||
self.test_corrupt_decrypt(tv)
|
||||
|
||||
|
||||
class TestOtherCiphers(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def create_test(cls, name, factory, key_size):
|
||||
|
||||
def test_template(self, factory=factory, key_size=key_size):
|
||||
cipher = factory.new(get_tag_random("cipher", key_size),
|
||||
factory.MODE_EAX,
|
||||
nonce=b"nonce")
|
||||
ct, mac = cipher.encrypt_and_digest(b"plaintext")
|
||||
|
||||
cipher = factory.new(get_tag_random("cipher", key_size),
|
||||
factory.MODE_EAX,
|
||||
nonce=b"nonce")
|
||||
pt2 = cipher.decrypt_and_verify(ct, mac)
|
||||
|
||||
self.assertEqual(b"plaintext", pt2)
|
||||
|
||||
setattr(cls, "test_" + name, test_template)
|
||||
|
||||
|
||||
from Crypto.Cipher import DES, DES3, ARC2, CAST, Blowfish
|
||||
|
||||
TestOtherCiphers.create_test("DES_" + str(DES.key_size), DES, DES.key_size)
|
||||
for ks in DES3.key_size:
|
||||
TestOtherCiphers.create_test("DES3_" + str(ks), DES3, ks)
|
||||
for ks in ARC2.key_size:
|
||||
TestOtherCiphers.create_test("ARC2_" + str(ks), ARC2, ks)
|
||||
for ks in CAST.key_size:
|
||||
TestOtherCiphers.create_test("CAST_" + str(ks), CAST, ks)
|
||||
for ks in Blowfish.key_size:
|
||||
TestOtherCiphers.create_test("Blowfish_" + str(ks), Blowfish, ks)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
wycheproof_warnings = config.get('wycheproof_warnings')
|
||||
|
||||
tests = []
|
||||
tests += list_test_cases(EaxTests)
|
||||
tests += list_test_cases(EaxFSMTests)
|
||||
tests += [ TestVectorsPaper() ]
|
||||
tests += [ TestVectorsWycheproof(wycheproof_warnings) ]
|
||||
tests += list_test_cases(TestOtherCiphers)
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,951 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.SelfTest.loader import load_test_vectors, load_test_vectors_wycheproof
|
||||
|
||||
from Crypto.Util.py3compat import tobytes, bchr
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Hash import SHAKE128, SHA256
|
||||
|
||||
from Crypto.Util.strxor import strxor
|
||||
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
|
||||
class GcmTests(unittest.TestCase):
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
nonce_96 = get_tag_random("nonce_128", 12)
|
||||
data = get_tag_random("data", 128)
|
||||
|
||||
def test_loopback_128(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
pt = get_tag_random("plaintext", 16 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_nonce(self):
|
||||
# Nonce is optional (a random one will be created)
|
||||
AES.new(self.key_128, AES.MODE_GCM)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, self.nonce_96)
|
||||
ct = cipher.encrypt(self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
self.assertEqual(ct, cipher.encrypt(self.data))
|
||||
|
||||
def test_nonce_must_be_bytes(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_GCM,
|
||||
nonce=u'test12345678')
|
||||
|
||||
def test_nonce_length(self):
|
||||
# nonce can be of any length (but not empty)
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_GCM,
|
||||
nonce=b"")
|
||||
|
||||
for x in range(1, 128):
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=bchr(1) * x)
|
||||
cipher.encrypt(bchr(1))
|
||||
|
||||
def test_block_size_128(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.block_size, AES.block_size)
|
||||
|
||||
def test_nonce_attribute(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.nonce, self.nonce_96)
|
||||
|
||||
# By default, a 15 bytes long nonce is randomly generated
|
||||
nonce1 = AES.new(self.key_128, AES.MODE_GCM).nonce
|
||||
nonce2 = AES.new(self.key_128, AES.MODE_GCM).nonce
|
||||
self.assertEqual(len(nonce1), 16)
|
||||
self.assertNotEqual(nonce1, nonce2)
|
||||
|
||||
def test_unknown_parameters(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_GCM,
|
||||
self.nonce_96, 7)
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96, unknown=7)
|
||||
|
||||
# But some are only known by the base cipher
|
||||
# (e.g. use_aesni consumed by the AES module)
|
||||
AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96,
|
||||
use_aesni=False)
|
||||
|
||||
def test_null_encryption_decryption(self):
|
||||
for func in "encrypt", "decrypt":
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
result = getattr(cipher, func)(b"")
|
||||
self.assertEqual(result, b"")
|
||||
|
||||
def test_either_encrypt_or_decrypt(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.encrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.decrypt, b"")
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.decrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.encrypt, b"")
|
||||
|
||||
def test_data_must_be_bytes(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*')
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*')
|
||||
|
||||
def test_mac_len(self):
|
||||
# Invalid MAC length
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96, mac_len=3)
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96, mac_len=16+1)
|
||||
|
||||
# Valid MAC length
|
||||
for mac_len in range(5, 16 + 1):
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96,
|
||||
mac_len=mac_len)
|
||||
_, mac = cipher.encrypt_and_digest(self.data)
|
||||
self.assertEqual(len(mac), mac_len)
|
||||
|
||||
# Default MAC length
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
_, mac = cipher.encrypt_and_digest(self.data)
|
||||
self.assertEqual(len(mac), 16)
|
||||
|
||||
def test_invalid_mac(self):
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
invalid_mac = strxor_c(mac, 0x01)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct,
|
||||
invalid_mac)
|
||||
|
||||
def test_hex_mac(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
mac_hex = cipher.hexdigest()
|
||||
self.assertEqual(cipher.digest(), unhexlify(mac_hex))
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.hexverify(mac_hex)
|
||||
|
||||
def test_message_chunks(self):
|
||||
# Validate that both associated data and plaintext/ciphertext
|
||||
# can be broken up in chunks of arbitrary length
|
||||
|
||||
auth_data = get_tag_random("authenticated data", 127)
|
||||
plaintext = get_tag_random("plaintext", 127)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.update(auth_data)
|
||||
ciphertext, ref_mac = cipher.encrypt_and_digest(plaintext)
|
||||
|
||||
def break_up(data, chunk_length):
|
||||
return [data[i:i+chunk_length] for i in range(0, len(data),
|
||||
chunk_length)]
|
||||
|
||||
# Encryption
|
||||
for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128:
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
|
||||
for chunk in break_up(auth_data, chunk_length):
|
||||
cipher.update(chunk)
|
||||
pt2 = b""
|
||||
for chunk in break_up(ciphertext, chunk_length):
|
||||
pt2 += cipher.decrypt(chunk)
|
||||
self.assertEqual(plaintext, pt2)
|
||||
cipher.verify(ref_mac)
|
||||
|
||||
# Decryption
|
||||
for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128:
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
|
||||
for chunk in break_up(auth_data, chunk_length):
|
||||
cipher.update(chunk)
|
||||
ct2 = b""
|
||||
for chunk in break_up(plaintext, chunk_length):
|
||||
ct2 += cipher.encrypt(chunk)
|
||||
self.assertEqual(ciphertext, ct2)
|
||||
self.assertEqual(cipher.digest(), ref_mac)
|
||||
|
||||
def test_bytearray(self):
|
||||
|
||||
# Encrypt
|
||||
key_ba = bytearray(self.key_128)
|
||||
nonce_ba = bytearray(self.nonce_96)
|
||||
header_ba = bytearray(self.data)
|
||||
data_ba = bytearray(self.data)
|
||||
|
||||
cipher1 = AES.new(self.key_128,
|
||||
AES.MODE_GCM,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data)
|
||||
ct = cipher1.encrypt(self.data)
|
||||
tag = cipher1.digest()
|
||||
|
||||
cipher2 = AES.new(key_ba,
|
||||
AES.MODE_GCM,
|
||||
nonce=nonce_ba)
|
||||
key_ba[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_ba[:3] = b"\xFF\xFF\xFF"
|
||||
cipher2.update(header_ba)
|
||||
header_ba[:3] = b"\xFF\xFF\xFF"
|
||||
ct_test = cipher2.encrypt(data_ba)
|
||||
data_ba[:3] = b"\xFF\xFF\xFF"
|
||||
tag_test = cipher2.digest()
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key_ba = bytearray(self.key_128)
|
||||
nonce_ba = bytearray(self.nonce_96)
|
||||
header_ba = bytearray(self.data)
|
||||
del data_ba
|
||||
|
||||
cipher4 = AES.new(key_ba,
|
||||
AES.MODE_GCM,
|
||||
nonce=nonce_ba)
|
||||
key_ba[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_ba[:3] = b"\xFF\xFF\xFF"
|
||||
cipher4.update(header_ba)
|
||||
header_ba[:3] = b"\xFF\xFF\xFF"
|
||||
pt_test = cipher4.decrypt_and_verify(bytearray(ct_test), bytearray(tag_test))
|
||||
|
||||
self.assertEqual(self.data, pt_test)
|
||||
|
||||
def test_memoryview(self):
|
||||
|
||||
# Encrypt
|
||||
key_mv = memoryview(bytearray(self.key_128))
|
||||
nonce_mv = memoryview(bytearray(self.nonce_96))
|
||||
header_mv = memoryview(bytearray(self.data))
|
||||
data_mv = memoryview(bytearray(self.data))
|
||||
|
||||
cipher1 = AES.new(self.key_128,
|
||||
AES.MODE_GCM,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data)
|
||||
ct = cipher1.encrypt(self.data)
|
||||
tag = cipher1.digest()
|
||||
|
||||
cipher2 = AES.new(key_mv,
|
||||
AES.MODE_GCM,
|
||||
nonce=nonce_mv)
|
||||
key_mv[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_mv[:3] = b"\xFF\xFF\xFF"
|
||||
cipher2.update(header_mv)
|
||||
header_mv[:3] = b"\xFF\xFF\xFF"
|
||||
ct_test = cipher2.encrypt(data_mv)
|
||||
data_mv[:3] = b"\xFF\xFF\xFF"
|
||||
tag_test = cipher2.digest()
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key_mv = memoryview(bytearray(self.key_128))
|
||||
nonce_mv = memoryview(bytearray(self.nonce_96))
|
||||
header_mv = memoryview(bytearray(self.data))
|
||||
del data_mv
|
||||
|
||||
cipher4 = AES.new(key_mv,
|
||||
AES.MODE_GCM,
|
||||
nonce=nonce_mv)
|
||||
key_mv[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_mv[:3] = b"\xFF\xFF\xFF"
|
||||
cipher4.update(header_mv)
|
||||
header_mv[:3] = b"\xFF\xFF\xFF"
|
||||
pt_test = cipher4.decrypt_and_verify(memoryview(ct_test), memoryview(tag_test))
|
||||
|
||||
self.assertEqual(self.data, pt_test)
|
||||
|
||||
def test_output_param(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(pt)
|
||||
tag = cipher.digest()
|
||||
|
||||
output = bytearray(128)
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
res, tag_out = cipher.encrypt_and_digest(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
self.assertEqual(tag, tag_out)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
res = cipher.decrypt_and_verify(ct, tag, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
def test_output_param_memoryview(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = memoryview(bytearray(128))
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
def test_output_param_neg(self):
|
||||
LEN_PT = 128
|
||||
|
||||
pt = b'5' * LEN_PT
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0' * LEN_PT)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0' * LEN_PT)
|
||||
|
||||
shorter_output = bytearray(LEN_PT - 1)
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
class GcmFSMTests(unittest.TestCase):
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
nonce_96 = get_tag_random("nonce_128", 12)
|
||||
data = get_tag_random("data", 128)
|
||||
|
||||
def test_valid_init_encrypt_decrypt_digest_verify(self):
|
||||
# No authenticated data, fixed plaintext
|
||||
# Verify path INIT->ENCRYPT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->DECRYPT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_init_update_digest_verify(self):
|
||||
# No plaintext, fixed authenticated data
|
||||
# Verify path INIT->UPDATE->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_full_path(self):
|
||||
# Fixed authenticated data, fixed plaintext
|
||||
# Verify path INIT->UPDATE->ENCRYPT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
ct = cipher.encrypt(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->DECRYPT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_init_digest(self):
|
||||
# Verify path INIT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.digest()
|
||||
|
||||
def test_valid_init_verify(self):
|
||||
# Verify path INIT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
mac = cipher.digest()
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_multiple_encrypt_or_decrypt(self):
|
||||
for method_name in "encrypt", "decrypt":
|
||||
for auth_data in (None, b"333", self.data,
|
||||
self.data + b"3"):
|
||||
if auth_data is None:
|
||||
assoc_len = None
|
||||
else:
|
||||
assoc_len = len(auth_data)
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96)
|
||||
if auth_data is not None:
|
||||
cipher.update(auth_data)
|
||||
method = getattr(cipher, method_name)
|
||||
method(self.data)
|
||||
method(self.data)
|
||||
method(self.data)
|
||||
method(self.data)
|
||||
|
||||
def test_valid_multiple_digest_or_verify(self):
|
||||
# Multiple calls to digest
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
first_mac = cipher.digest()
|
||||
for x in range(4):
|
||||
self.assertEqual(first_mac, cipher.digest())
|
||||
|
||||
# Multiple calls to verify
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
for x in range(5):
|
||||
cipher.verify(first_mac)
|
||||
|
||||
def test_valid_encrypt_and_digest_decrypt_and_verify(self):
|
||||
# encrypt_and_digest
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
# decrypt_and_verify
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
pt = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(self.data, pt)
|
||||
|
||||
def test_invalid_mixing_encrypt_decrypt(self):
|
||||
# Once per method, with or without assoc. data
|
||||
for method1_name, method2_name in (("encrypt", "decrypt"),
|
||||
("decrypt", "encrypt")):
|
||||
for assoc_data_present in (True, False):
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM,
|
||||
nonce=self.nonce_96)
|
||||
if assoc_data_present:
|
||||
cipher.update(self.data)
|
||||
getattr(cipher, method1_name)(self.data)
|
||||
self.assertRaises(TypeError, getattr(cipher, method2_name),
|
||||
self.data)
|
||||
|
||||
def test_invalid_encrypt_or_update_after_digest(self):
|
||||
for method_name in "encrypt", "update":
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.encrypt(self.data)
|
||||
cipher.digest()
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.encrypt_and_digest(self.data)
|
||||
|
||||
def test_invalid_decrypt_or_update_after_verify(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
for method_name in "decrypt", "update":
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
|
||||
cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data)
|
||||
|
||||
|
||||
class TestVectors(unittest.TestCase):
|
||||
"""Class exercising the GCM test vectors found in
|
||||
http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf"""
|
||||
|
||||
# List of test vectors, each made up of:
|
||||
# - authenticated data
|
||||
# - plaintext
|
||||
# - ciphertext
|
||||
# - MAC
|
||||
# - AES key
|
||||
# - nonce
|
||||
test_vectors_hex = [
|
||||
(
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'58e2fccefa7e3061367f1d57a4e7455a',
|
||||
'00000000000000000000000000000000',
|
||||
'000000000000000000000000'
|
||||
),
|
||||
(
|
||||
'',
|
||||
'00000000000000000000000000000000',
|
||||
'0388dace60b6a392f328c2b971b2fe78',
|
||||
'ab6e47d42cec13bdf53a67b21257bddf',
|
||||
'00000000000000000000000000000000',
|
||||
'000000000000000000000000'
|
||||
),
|
||||
(
|
||||
'',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255',
|
||||
'42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e' +
|
||||
'21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985',
|
||||
'4d5c2af327cd64a62cf35abd2ba6fab4',
|
||||
'feffe9928665731c6d6a8f9467308308',
|
||||
'cafebabefacedbaddecaf888'
|
||||
),
|
||||
(
|
||||
'feedfacedeadbeeffeedfacedeadbeefabaddad2',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39',
|
||||
'42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e' +
|
||||
'21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091',
|
||||
'5bc94fbc3221a5db94fae95ae7121a47',
|
||||
'feffe9928665731c6d6a8f9467308308',
|
||||
'cafebabefacedbaddecaf888'
|
||||
),
|
||||
(
|
||||
'feedfacedeadbeeffeedfacedeadbeefabaddad2',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39',
|
||||
'61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c7423' +
|
||||
'73806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598',
|
||||
'3612d2e79e3b0785561be14aaca2fccb',
|
||||
'feffe9928665731c6d6a8f9467308308',
|
||||
'cafebabefacedbad'
|
||||
),
|
||||
(
|
||||
'feedfacedeadbeeffeedfacedeadbeefabaddad2',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39',
|
||||
'8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca7' +
|
||||
'01e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5',
|
||||
'619cc5aefffe0bfa462af43c1699d050',
|
||||
'feffe9928665731c6d6a8f9467308308',
|
||||
'9313225df88406e555909c5aff5269aa' +
|
||||
'6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b5254' +
|
||||
'16aedbf5a0de6a57a637b39b'
|
||||
),
|
||||
(
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'cd33b28ac773f74ba00ed1f312572435',
|
||||
'000000000000000000000000000000000000000000000000',
|
||||
'000000000000000000000000'
|
||||
),
|
||||
(
|
||||
'',
|
||||
'00000000000000000000000000000000',
|
||||
'98e7247c07f0fe411c267e4384b0f600',
|
||||
'2ff58d80033927ab8ef4d4587514f0fb',
|
||||
'000000000000000000000000000000000000000000000000',
|
||||
'000000000000000000000000'
|
||||
),
|
||||
(
|
||||
'',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255',
|
||||
'3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c' +
|
||||
'7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256',
|
||||
'9924a7c8587336bfb118024db8674a14',
|
||||
'feffe9928665731c6d6a8f9467308308feffe9928665731c',
|
||||
'cafebabefacedbaddecaf888'
|
||||
),
|
||||
(
|
||||
'feedfacedeadbeeffeedfacedeadbeefabaddad2',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39',
|
||||
'3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c' +
|
||||
'7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710',
|
||||
'2519498e80f1478f37ba55bd6d27618c',
|
||||
'feffe9928665731c6d6a8f9467308308feffe9928665731c',
|
||||
'cafebabefacedbaddecaf888'
|
||||
),
|
||||
(
|
||||
'feedfacedeadbeeffeedfacedeadbeefabaddad2',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39',
|
||||
'0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057' +
|
||||
'fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7',
|
||||
'65dcc57fcf623a24094fcca40d3533f8',
|
||||
'feffe9928665731c6d6a8f9467308308feffe9928665731c',
|
||||
'cafebabefacedbad'
|
||||
),
|
||||
(
|
||||
'feedfacedeadbeeffeedfacedeadbeefabaddad2',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39',
|
||||
'd27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e45' +
|
||||
'81e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b',
|
||||
'dcf566ff291c25bbb8568fc3d376a6d9',
|
||||
'feffe9928665731c6d6a8f9467308308feffe9928665731c',
|
||||
'9313225df88406e555909c5aff5269aa' +
|
||||
'6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b5254' +
|
||||
'16aedbf5a0de6a57a637b39b'
|
||||
),
|
||||
(
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'530f8afbc74536b9a963b4f1c4cb738b',
|
||||
'0000000000000000000000000000000000000000000000000000000000000000',
|
||||
'000000000000000000000000'
|
||||
),
|
||||
(
|
||||
'',
|
||||
'00000000000000000000000000000000',
|
||||
'cea7403d4d606b6e074ec5d3baf39d18',
|
||||
'd0d1c8a799996bf0265b98b5d48ab919',
|
||||
'0000000000000000000000000000000000000000000000000000000000000000',
|
||||
'000000000000000000000000'
|
||||
),
|
||||
( '',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255',
|
||||
'522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa' +
|
||||
'8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad',
|
||||
'b094dac5d93471bdec1a502270e3cc6c',
|
||||
'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308',
|
||||
'cafebabefacedbaddecaf888'
|
||||
),
|
||||
(
|
||||
'feedfacedeadbeeffeedfacedeadbeefabaddad2',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39',
|
||||
'522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa' +
|
||||
'8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662',
|
||||
'76fc6ece0f4e1768cddf8853bb2d551b',
|
||||
'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308',
|
||||
'cafebabefacedbaddecaf888'
|
||||
),
|
||||
(
|
||||
'feedfacedeadbeeffeedfacedeadbeefabaddad2',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39',
|
||||
'c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0' +
|
||||
'feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f',
|
||||
'3a337dbf46a792c45e454913fe2ea8f2',
|
||||
'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308',
|
||||
'cafebabefacedbad'
|
||||
),
|
||||
(
|
||||
'feedfacedeadbeeffeedfacedeadbeefabaddad2',
|
||||
'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' +
|
||||
'1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39',
|
||||
'5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf4' +
|
||||
'0fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f',
|
||||
'a44a8266ee1c8eb0c8b5d4cf5ae9f19a',
|
||||
'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308',
|
||||
'9313225df88406e555909c5aff5269aa' +
|
||||
'6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b5254' +
|
||||
'16aedbf5a0de6a57a637b39b'
|
||||
)
|
||||
]
|
||||
|
||||
test_vectors = [[unhexlify(x) for x in tv] for tv in test_vectors_hex]
|
||||
|
||||
def runTest(self):
|
||||
for assoc_data, pt, ct, mac, key, nonce in self.test_vectors:
|
||||
|
||||
# Encrypt
|
||||
cipher = AES.new(key, AES.MODE_GCM, nonce, mac_len=len(mac))
|
||||
cipher.update(assoc_data)
|
||||
ct2, mac2 = cipher.encrypt_and_digest(pt)
|
||||
self.assertEqual(ct, ct2)
|
||||
self.assertEqual(mac, mac2)
|
||||
|
||||
# Decrypt
|
||||
cipher = AES.new(key, AES.MODE_GCM, nonce, mac_len=len(mac))
|
||||
cipher.update(assoc_data)
|
||||
pt2 = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
|
||||
class TestVectorsGueronKrasnov(unittest.TestCase):
|
||||
"""Class exercising the GCM test vectors found in
|
||||
'The fragility of AES-GCM authentication algorithm', Gueron, Krasnov
|
||||
https://eprint.iacr.org/2013/157.pdf"""
|
||||
|
||||
def test_1(self):
|
||||
key = unhexlify("3da6c536d6295579c0959a7043efb503")
|
||||
iv = unhexlify("2b926197d34e091ef722db94")
|
||||
aad = unhexlify("00000000000000000000000000000000" +
|
||||
"000102030405060708090a0b0c0d0e0f" +
|
||||
"101112131415161718191a1b1c1d1e1f" +
|
||||
"202122232425262728292a2b2c2d2e2f" +
|
||||
"303132333435363738393a3b3c3d3e3f")
|
||||
digest = unhexlify("69dd586555ce3fcc89663801a71d957b")
|
||||
|
||||
cipher = AES.new(key, AES.MODE_GCM, iv).update(aad)
|
||||
self.assertEqual(digest, cipher.digest())
|
||||
|
||||
def test_2(self):
|
||||
key = unhexlify("843ffcf5d2b72694d19ed01d01249412")
|
||||
iv = unhexlify("dbcca32ebf9b804617c3aa9e")
|
||||
aad = unhexlify("00000000000000000000000000000000" +
|
||||
"101112131415161718191a1b1c1d1e1f")
|
||||
pt = unhexlify("000102030405060708090a0b0c0d0e0f" +
|
||||
"101112131415161718191a1b1c1d1e1f" +
|
||||
"202122232425262728292a2b2c2d2e2f" +
|
||||
"303132333435363738393a3b3c3d3e3f" +
|
||||
"404142434445464748494a4b4c4d4e4f")
|
||||
ct = unhexlify("6268c6fa2a80b2d137467f092f657ac0" +
|
||||
"4d89be2beaa623d61b5a868c8f03ff95" +
|
||||
"d3dcee23ad2f1ab3a6c80eaf4b140eb0" +
|
||||
"5de3457f0fbc111a6b43d0763aa422a3" +
|
||||
"013cf1dc37fe417d1fbfc449b75d4cc5")
|
||||
digest = unhexlify("3b629ccfbc1119b7319e1dce2cd6fd6d")
|
||||
|
||||
cipher = AES.new(key, AES.MODE_GCM, iv).update(aad)
|
||||
ct2, digest2 = cipher.encrypt_and_digest(pt)
|
||||
|
||||
self.assertEqual(ct, ct2)
|
||||
self.assertEqual(digest, digest2)
|
||||
|
||||
|
||||
class NISTTestVectorsGCM(unittest.TestCase):
|
||||
|
||||
def __init__(self, a):
|
||||
self.use_clmul = True
|
||||
unittest.TestCase.__init__(self, a)
|
||||
|
||||
|
||||
class NISTTestVectorsGCM_no_clmul(unittest.TestCase):
|
||||
|
||||
def __init__(self, a):
|
||||
self.use_clmul = False
|
||||
unittest.TestCase.__init__(self, a)
|
||||
|
||||
|
||||
test_vectors_nist = load_test_vectors(
|
||||
("Cipher", "AES"),
|
||||
"gcmDecrypt128.rsp",
|
||||
"GCM decrypt",
|
||||
{"count": lambda x: int(x)}) or []
|
||||
|
||||
test_vectors_nist += load_test_vectors(
|
||||
("Cipher", "AES"),
|
||||
"gcmEncryptExtIV128.rsp",
|
||||
"GCM encrypt",
|
||||
{"count": lambda x: int(x)}) or []
|
||||
|
||||
for idx, tv in enumerate(test_vectors_nist):
|
||||
|
||||
# The test vector file contains some directive lines
|
||||
if isinstance(tv, str):
|
||||
continue
|
||||
|
||||
def single_test(self, tv=tv):
|
||||
|
||||
self.description = tv.desc
|
||||
cipher = AES.new(tv.key, AES.MODE_GCM, nonce=tv.iv,
|
||||
mac_len=len(tv.tag), use_clmul=self.use_clmul)
|
||||
cipher.update(tv.aad)
|
||||
if "FAIL" in tv.others:
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify,
|
||||
tv.ct, tv.tag)
|
||||
else:
|
||||
pt = cipher.decrypt_and_verify(tv.ct, tv.tag)
|
||||
self.assertEqual(pt, tv.pt)
|
||||
|
||||
setattr(NISTTestVectorsGCM, "test_%d" % idx, single_test)
|
||||
setattr(NISTTestVectorsGCM_no_clmul, "test_%d" % idx, single_test)
|
||||
|
||||
|
||||
class TestVectorsWycheproof(unittest.TestCase):
|
||||
|
||||
def __init__(self, wycheproof_warnings, **extra_params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self._wycheproof_warnings = wycheproof_warnings
|
||||
self._extra_params = extra_params
|
||||
self._id = "None"
|
||||
|
||||
def setUp(self):
|
||||
|
||||
def filter_tag(group):
|
||||
return group['tagSize'] // 8
|
||||
|
||||
self.tv = load_test_vectors_wycheproof(("Cipher", "wycheproof"),
|
||||
"aes_gcm_test.json",
|
||||
"Wycheproof GCM",
|
||||
group_tag={'tag_size': filter_tag})
|
||||
|
||||
def shortDescription(self):
|
||||
return self._id
|
||||
|
||||
def warn(self, tv):
|
||||
if tv.warning and self._wycheproof_warnings:
|
||||
import warnings
|
||||
warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment))
|
||||
|
||||
def test_encrypt(self, tv):
|
||||
self._id = "Wycheproof Encrypt GCM Test #" + str(tv.id)
|
||||
|
||||
try:
|
||||
cipher = AES.new(tv.key, AES.MODE_GCM, tv.iv, mac_len=tv.tag_size,
|
||||
**self._extra_params)
|
||||
except ValueError as e:
|
||||
if len(tv.iv) == 0 and "Nonce cannot be empty" in str(e):
|
||||
return
|
||||
raise e
|
||||
|
||||
cipher.update(tv.aad)
|
||||
ct, tag = cipher.encrypt_and_digest(tv.msg)
|
||||
if tv.valid:
|
||||
self.assertEqual(ct, tv.ct)
|
||||
self.assertEqual(tag, tv.tag)
|
||||
self.warn(tv)
|
||||
|
||||
def test_decrypt(self, tv):
|
||||
self._id = "Wycheproof Decrypt GCM Test #" + str(tv.id)
|
||||
|
||||
try:
|
||||
cipher = AES.new(tv.key, AES.MODE_GCM, tv.iv, mac_len=tv.tag_size,
|
||||
**self._extra_params)
|
||||
except ValueError as e:
|
||||
if len(tv.iv) == 0 and "Nonce cannot be empty" in str(e):
|
||||
return
|
||||
raise e
|
||||
|
||||
cipher.update(tv.aad)
|
||||
try:
|
||||
pt = cipher.decrypt_and_verify(tv.ct, tv.tag)
|
||||
except ValueError:
|
||||
assert not tv.valid
|
||||
else:
|
||||
assert tv.valid
|
||||
self.assertEqual(pt, tv.msg)
|
||||
self.warn(tv)
|
||||
|
||||
def test_corrupt_decrypt(self, tv):
|
||||
self._id = "Wycheproof Corrupt Decrypt GCM Test #" + str(tv.id)
|
||||
if len(tv.iv) == 0 or len(tv.ct) < 1:
|
||||
return
|
||||
cipher = AES.new(tv.key, AES.MODE_GCM, tv.iv, mac_len=tv.tag_size,
|
||||
**self._extra_params)
|
||||
cipher.update(tv.aad)
|
||||
ct_corrupt = strxor(tv.ct, b"\x00" * (len(tv.ct) - 1) + b"\x01")
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct_corrupt, tv.tag)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
for tv in self.tv:
|
||||
self.test_encrypt(tv)
|
||||
self.test_decrypt(tv)
|
||||
self.test_corrupt_decrypt(tv)
|
||||
|
||||
|
||||
class TestVariableLength(unittest.TestCase):
|
||||
|
||||
def __init__(self, **extra_params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self._extra_params = extra_params
|
||||
|
||||
def runTest(self):
|
||||
key = b'0' * 16
|
||||
h = SHA256.new()
|
||||
|
||||
for length in range(160):
|
||||
nonce = '{0:04d}'.format(length).encode('utf-8')
|
||||
data = bchr(length) * length
|
||||
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce, **self._extra_params)
|
||||
ct, tag = cipher.encrypt_and_digest(data)
|
||||
h.update(ct)
|
||||
h.update(tag)
|
||||
|
||||
self.assertEqual(h.hexdigest(), "7b7eb1ffbe67a2e53a912067c0ec8e62ebc7ce4d83490ea7426941349811bdf4")
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Util import _cpu_features
|
||||
|
||||
wycheproof_warnings = config.get('wycheproof_warnings')
|
||||
|
||||
tests = []
|
||||
tests += list_test_cases(GcmTests)
|
||||
tests += list_test_cases(GcmFSMTests)
|
||||
tests += [TestVectors()]
|
||||
tests += [TestVectorsWycheproof(wycheproof_warnings)]
|
||||
tests += list_test_cases(TestVectorsGueronKrasnov)
|
||||
tests += [TestVariableLength()]
|
||||
if config.get('slow_tests'):
|
||||
tests += list_test_cases(NISTTestVectorsGCM)
|
||||
|
||||
if _cpu_features.have_clmul():
|
||||
tests += [TestVectorsWycheproof(wycheproof_warnings, use_clmul=False)]
|
||||
tests += [TestVariableLength(use_clmul=False)]
|
||||
if config.get('slow_tests'):
|
||||
tests += list_test_cases(NISTTestVectorsGCM_no_clmul)
|
||||
else:
|
||||
print("Skipping test of PCLMULDQD in AES GCM")
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def suite():
|
||||
unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,845 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.Util.py3compat import b, tobytes, bchr
|
||||
from Crypto.Util.number import long_to_bytes
|
||||
from Crypto.SelfTest.loader import load_test_vectors
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Hash import SHAKE128
|
||||
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
|
||||
class OcbTests(unittest.TestCase):
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
nonce_96 = get_tag_random("nonce_128", 12)
|
||||
data = get_tag_random("data", 128)
|
||||
|
||||
def test_loopback_128(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
pt = get_tag_random("plaintext", 16 * 100)
|
||||
ct, mac = cipher.encrypt_and_digest(pt)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
pt2 = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_nonce(self):
|
||||
# Nonce is optional
|
||||
AES.new(self.key_128, AES.MODE_OCB)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, self.nonce_96)
|
||||
ct = cipher.encrypt(self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
self.assertEqual(ct, cipher.encrypt(self.data))
|
||||
|
||||
def test_nonce_must_be_bytes(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_OCB,
|
||||
nonce=u'test12345678')
|
||||
|
||||
def test_nonce_length(self):
|
||||
# nonce cannot be empty
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_OCB,
|
||||
nonce=b(""))
|
||||
|
||||
# nonce can be up to 15 bytes long
|
||||
for length in range(1, 16):
|
||||
AES.new(self.key_128, AES.MODE_OCB, nonce=self.data[:length])
|
||||
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_OCB,
|
||||
nonce=self.data)
|
||||
|
||||
def test_block_size_128(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.block_size, AES.block_size)
|
||||
|
||||
# By default, a 15 bytes long nonce is randomly generated
|
||||
nonce1 = AES.new(self.key_128, AES.MODE_OCB).nonce
|
||||
nonce2 = AES.new(self.key_128, AES.MODE_OCB).nonce
|
||||
self.assertEqual(len(nonce1), 15)
|
||||
self.assertNotEqual(nonce1, nonce2)
|
||||
|
||||
def test_nonce_attribute(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.nonce, self.nonce_96)
|
||||
|
||||
# By default, a 15 bytes long nonce is randomly generated
|
||||
nonce1 = AES.new(self.key_128, AES.MODE_OCB).nonce
|
||||
nonce2 = AES.new(self.key_128, AES.MODE_OCB).nonce
|
||||
self.assertEqual(len(nonce1), 15)
|
||||
self.assertNotEqual(nonce1, nonce2)
|
||||
|
||||
def test_unknown_parameters(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_OCB,
|
||||
self.nonce_96, 7)
|
||||
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96, unknown=7)
|
||||
|
||||
# But some are only known by the base cipher
|
||||
# (e.g. use_aesni consumed by the AES module)
|
||||
AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96,
|
||||
use_aesni=False)
|
||||
|
||||
def test_null_encryption_decryption(self):
|
||||
for func in "encrypt", "decrypt":
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
result = getattr(cipher, func)(b(""))
|
||||
self.assertEqual(result, b(""))
|
||||
|
||||
def test_either_encrypt_or_decrypt(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.encrypt(b("xyz"))
|
||||
self.assertRaises(TypeError, cipher.decrypt, b("xyz"))
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.decrypt(b("xyz"))
|
||||
self.assertRaises(TypeError, cipher.encrypt, b("xyz"))
|
||||
|
||||
def test_data_must_be_bytes(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*')
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*')
|
||||
|
||||
def test_mac_len(self):
|
||||
# Invalid MAC length
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96, mac_len=7)
|
||||
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96, mac_len=16+1)
|
||||
|
||||
# Valid MAC length
|
||||
for mac_len in range(8, 16 + 1):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96,
|
||||
mac_len=mac_len)
|
||||
_, mac = cipher.encrypt_and_digest(self.data)
|
||||
self.assertEqual(len(mac), mac_len)
|
||||
|
||||
# Default MAC length
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
_, mac = cipher.encrypt_and_digest(self.data)
|
||||
self.assertEqual(len(mac), 16)
|
||||
|
||||
def test_invalid_mac(self):
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
invalid_mac = strxor_c(mac, 0x01)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct,
|
||||
invalid_mac)
|
||||
|
||||
def test_hex_mac(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
mac_hex = cipher.hexdigest()
|
||||
self.assertEqual(cipher.digest(), unhexlify(mac_hex))
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.hexverify(mac_hex)
|
||||
|
||||
def test_message_chunks(self):
|
||||
# Validate that both associated data and plaintext/ciphertext
|
||||
# can be broken up in chunks of arbitrary length
|
||||
|
||||
auth_data = get_tag_random("authenticated data", 127)
|
||||
plaintext = get_tag_random("plaintext", 127)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.update(auth_data)
|
||||
ciphertext, ref_mac = cipher.encrypt_and_digest(plaintext)
|
||||
|
||||
def break_up(data, chunk_length):
|
||||
return [data[i:i+chunk_length] for i in range(0, len(data),
|
||||
chunk_length)]
|
||||
|
||||
# Encryption
|
||||
for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128:
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
|
||||
for chunk in break_up(auth_data, chunk_length):
|
||||
cipher.update(chunk)
|
||||
pt2 = b("")
|
||||
for chunk in break_up(ciphertext, chunk_length):
|
||||
pt2 += cipher.decrypt(chunk)
|
||||
pt2 += cipher.decrypt()
|
||||
self.assertEqual(plaintext, pt2)
|
||||
cipher.verify(ref_mac)
|
||||
|
||||
# Decryption
|
||||
for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128:
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
|
||||
for chunk in break_up(auth_data, chunk_length):
|
||||
cipher.update(chunk)
|
||||
ct2 = b("")
|
||||
for chunk in break_up(plaintext, chunk_length):
|
||||
ct2 += cipher.encrypt(chunk)
|
||||
ct2 += cipher.encrypt()
|
||||
self.assertEqual(ciphertext, ct2)
|
||||
self.assertEqual(cipher.digest(), ref_mac)
|
||||
|
||||
def test_bytearray(self):
|
||||
|
||||
# Encrypt
|
||||
key_ba = bytearray(self.key_128)
|
||||
nonce_ba = bytearray(self.nonce_96)
|
||||
header_ba = bytearray(self.data)
|
||||
data_ba = bytearray(self.data)
|
||||
|
||||
cipher1 = AES.new(self.key_128,
|
||||
AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data)
|
||||
ct = cipher1.encrypt(self.data) + cipher1.encrypt()
|
||||
tag = cipher1.digest()
|
||||
|
||||
cipher2 = AES.new(key_ba,
|
||||
AES.MODE_OCB,
|
||||
nonce=nonce_ba)
|
||||
key_ba[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_ba[:3] = b"\xFF\xFF\xFF"
|
||||
cipher2.update(header_ba)
|
||||
header_ba[:3] = b"\xFF\xFF\xFF"
|
||||
ct_test = cipher2.encrypt(data_ba) + cipher2.encrypt()
|
||||
data_ba[:3] = b"\xFF\xFF\xFF"
|
||||
tag_test = cipher2.digest()
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key_ba = bytearray(self.key_128)
|
||||
nonce_ba = bytearray(self.nonce_96)
|
||||
header_ba = bytearray(self.data)
|
||||
del data_ba
|
||||
|
||||
cipher4 = AES.new(key_ba,
|
||||
AES.MODE_OCB,
|
||||
nonce=nonce_ba)
|
||||
key_ba[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_ba[:3] = b"\xFF\xFF\xFF"
|
||||
cipher4.update(header_ba)
|
||||
header_ba[:3] = b"\xFF\xFF\xFF"
|
||||
pt_test = cipher4.decrypt_and_verify(bytearray(ct_test), bytearray(tag_test))
|
||||
|
||||
self.assertEqual(self.data, pt_test)
|
||||
|
||||
def test_memoryview(self):
|
||||
|
||||
# Encrypt
|
||||
key_mv = memoryview(bytearray(self.key_128))
|
||||
nonce_mv = memoryview(bytearray(self.nonce_96))
|
||||
header_mv = memoryview(bytearray(self.data))
|
||||
data_mv = memoryview(bytearray(self.data))
|
||||
|
||||
cipher1 = AES.new(self.key_128,
|
||||
AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data)
|
||||
ct = cipher1.encrypt(self.data) + cipher1.encrypt()
|
||||
tag = cipher1.digest()
|
||||
|
||||
cipher2 = AES.new(key_mv,
|
||||
AES.MODE_OCB,
|
||||
nonce=nonce_mv)
|
||||
key_mv[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_mv[:3] = b"\xFF\xFF\xFF"
|
||||
cipher2.update(header_mv)
|
||||
header_mv[:3] = b"\xFF\xFF\xFF"
|
||||
ct_test = cipher2.encrypt(data_mv) + cipher2.encrypt()
|
||||
data_mv[:3] = b"\xFF\xFF\xFF"
|
||||
tag_test = cipher2.digest()
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key_mv = memoryview(bytearray(self.key_128))
|
||||
nonce_mv = memoryview(bytearray(self.nonce_96))
|
||||
header_mv = memoryview(bytearray(self.data))
|
||||
del data_mv
|
||||
|
||||
cipher4 = AES.new(key_mv,
|
||||
AES.MODE_OCB,
|
||||
nonce=nonce_mv)
|
||||
key_mv[:3] = b"\xFF\xFF\xFF"
|
||||
nonce_mv[:3] = b"\xFF\xFF\xFF"
|
||||
cipher4.update(header_mv)
|
||||
header_mv[:3] = b"\xFF\xFF\xFF"
|
||||
pt_test = cipher4.decrypt_and_verify(memoryview(ct_test), memoryview(tag_test))
|
||||
|
||||
self.assertEqual(self.data, pt_test)
|
||||
|
||||
|
||||
class OcbFSMTests(unittest.TestCase):
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
nonce_96 = get_tag_random("nonce_128", 12)
|
||||
data = get_tag_random("data", 128)
|
||||
|
||||
def test_valid_init_encrypt_decrypt_digest_verify(self):
|
||||
# No authenticated data, fixed plaintext
|
||||
# Verify path INIT->ENCRYPT->ENCRYPT(NONE)->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data)
|
||||
ct += cipher.encrypt()
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->DECRYPT->DECRYPT(NONCE)->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
cipher.decrypt()
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_invalid_init_encrypt_decrypt_digest_verify(self):
|
||||
# No authenticated data, fixed plaintext
|
||||
# Verify path INIT->ENCRYPT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data)
|
||||
self.assertRaises(TypeError, cipher.digest)
|
||||
|
||||
# Verify path INIT->DECRYPT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
self.assertRaises(TypeError, cipher.verify)
|
||||
|
||||
def test_valid_init_update_digest_verify(self):
|
||||
# No plaintext, fixed authenticated data
|
||||
# Verify path INIT->UPDATE->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_full_path(self):
|
||||
# Fixed authenticated data, fixed plaintext
|
||||
# Verify path INIT->UPDATE->ENCRYPT->ENCRYPT(NONE)->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
ct = cipher.encrypt(self.data)
|
||||
ct += cipher.encrypt()
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->DECRYPT->DECRYPT(NONE)->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
cipher.decrypt(ct)
|
||||
cipher.decrypt()
|
||||
cipher.verify(mac)
|
||||
|
||||
# Verify path INIT->UPDATE->ENCRYPT->ENCRYPT_AND_DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
ct1 = cipher.encrypt(self.data[:2])
|
||||
ct2, mac = cipher.encrypt_and_digest(self.data[2:])
|
||||
|
||||
# Verify path INIT->UPDATE->DECRYPT->DECRYPT_AND_VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
cipher.decrypt(ct1)
|
||||
cipher.decrypt_and_verify(ct2, mac)
|
||||
|
||||
def test_invalid_encrypt_after_final(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
cipher.encrypt(self.data)
|
||||
cipher.encrypt()
|
||||
self.assertRaises(TypeError, cipher.encrypt, self.data)
|
||||
|
||||
def test_invalid_decrypt_after_final(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
cipher.decrypt(self.data)
|
||||
cipher.decrypt()
|
||||
self.assertRaises(TypeError, cipher.decrypt, self.data)
|
||||
|
||||
def test_valid_init_digest(self):
|
||||
# Verify path INIT->DIGEST
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.digest()
|
||||
|
||||
def test_valid_init_verify(self):
|
||||
# Verify path INIT->VERIFY
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
mac = cipher.digest()
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_multiple_encrypt_or_decrypt(self):
|
||||
for method_name in "encrypt", "decrypt":
|
||||
for auth_data in (None, b("333"), self.data,
|
||||
self.data + b("3")):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
if auth_data is not None:
|
||||
cipher.update(auth_data)
|
||||
method = getattr(cipher, method_name)
|
||||
method(self.data)
|
||||
method(self.data)
|
||||
method(self.data)
|
||||
method(self.data)
|
||||
method()
|
||||
|
||||
def test_valid_multiple_digest_or_verify(self):
|
||||
# Multiple calls to digest
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
first_mac = cipher.digest()
|
||||
for x in range(4):
|
||||
self.assertEqual(first_mac, cipher.digest())
|
||||
|
||||
# Multiple calls to verify
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
for x in range(5):
|
||||
cipher.verify(first_mac)
|
||||
|
||||
def test_valid_encrypt_and_digest_decrypt_and_verify(self):
|
||||
# encrypt_and_digest
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
# decrypt_and_verify
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
pt = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(self.data, pt)
|
||||
|
||||
def test_invalid_mixing_encrypt_decrypt(self):
|
||||
# Once per method, with or without assoc. data
|
||||
for method1_name, method2_name in (("encrypt", "decrypt"),
|
||||
("decrypt", "encrypt")):
|
||||
for assoc_data_present in (True, False):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB,
|
||||
nonce=self.nonce_96)
|
||||
if assoc_data_present:
|
||||
cipher.update(self.data)
|
||||
getattr(cipher, method1_name)(self.data)
|
||||
self.assertRaises(TypeError, getattr(cipher, method2_name),
|
||||
self.data)
|
||||
|
||||
def test_invalid_encrypt_or_update_after_digest(self):
|
||||
for method_name in "encrypt", "update":
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.encrypt(self.data)
|
||||
cipher.encrypt()
|
||||
cipher.digest()
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.encrypt_and_digest(self.data)
|
||||
|
||||
def test_invalid_decrypt_or_update_after_verify(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
ct = cipher.encrypt(self.data)
|
||||
ct += cipher.encrypt()
|
||||
mac = cipher.digest()
|
||||
|
||||
for method_name in "decrypt", "update":
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.decrypt(ct)
|
||||
cipher.decrypt()
|
||||
cipher.verify(mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96)
|
||||
cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertRaises(TypeError, getattr(cipher, method_name),
|
||||
self.data)
|
||||
|
||||
|
||||
def algo_rfc7253(keylen, taglen, noncelen):
|
||||
"""Implement the algorithm at page 18 of RFC 7253"""
|
||||
|
||||
key = bchr(0) * (keylen // 8 - 1) + bchr(taglen)
|
||||
C = b""
|
||||
|
||||
for i in range(128):
|
||||
S = bchr(0) * i
|
||||
|
||||
N = long_to_bytes(3 * i + 1, noncelen // 8)
|
||||
cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8)
|
||||
cipher.update(S)
|
||||
C += cipher.encrypt(S) + cipher.encrypt() + cipher.digest()
|
||||
|
||||
N = long_to_bytes(3 * i + 2, noncelen // 8)
|
||||
cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8)
|
||||
C += cipher.encrypt(S) + cipher.encrypt() + cipher.digest()
|
||||
|
||||
N = long_to_bytes(3 * i + 3, noncelen // 8)
|
||||
cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8)
|
||||
cipher.update(S)
|
||||
C += cipher.encrypt() + cipher.digest()
|
||||
|
||||
N = long_to_bytes(385, noncelen // 8)
|
||||
cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8)
|
||||
cipher.update(C)
|
||||
return cipher.encrypt() + cipher.digest()
|
||||
|
||||
|
||||
class OcbRfc7253Test(unittest.TestCase):
|
||||
|
||||
# Tuple with
|
||||
# - nonce
|
||||
# - authenticated data
|
||||
# - plaintext
|
||||
# - ciphertext and 16 byte MAC tag
|
||||
tv1_key = "000102030405060708090A0B0C0D0E0F"
|
||||
tv1 = (
|
||||
(
|
||||
"BBAA99887766554433221100",
|
||||
"",
|
||||
"",
|
||||
"785407BFFFC8AD9EDCC5520AC9111EE6"
|
||||
),
|
||||
(
|
||||
"BBAA99887766554433221101",
|
||||
"0001020304050607",
|
||||
"0001020304050607",
|
||||
"6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009"
|
||||
),
|
||||
(
|
||||
"BBAA99887766554433221102",
|
||||
"0001020304050607",
|
||||
"",
|
||||
"81017F8203F081277152FADE694A0A00"
|
||||
),
|
||||
(
|
||||
"BBAA99887766554433221103",
|
||||
"",
|
||||
"0001020304050607",
|
||||
"45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9"
|
||||
),
|
||||
(
|
||||
"BBAA99887766554433221104",
|
||||
"000102030405060708090A0B0C0D0E0F",
|
||||
"000102030405060708090A0B0C0D0E0F",
|
||||
"571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5"
|
||||
"701C1CCEC8FC3358"
|
||||
),
|
||||
(
|
||||
"BBAA99887766554433221105",
|
||||
"000102030405060708090A0B0C0D0E0F",
|
||||
"",
|
||||
"8CF761B6902EF764462AD86498CA6B97"
|
||||
),
|
||||
(
|
||||
"BBAA99887766554433221106",
|
||||
"",
|
||||
"000102030405060708090A0B0C0D0E0F",
|
||||
"5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436B"
|
||||
"DF06D8FA1ECA343D"
|
||||
),
|
||||
(
|
||||
"BBAA99887766554433221107",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617",
|
||||
"1CA2207308C87C010756104D8840CE1952F09673A448A122"
|
||||
"C92C62241051F57356D7F3C90BB0E07F"
|
||||
),
|
||||
(
|
||||
"BBAA99887766554433221108",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617",
|
||||
"",
|
||||
"6DC225A071FC1B9F7C69F93B0F1E10DE"
|
||||
),
|
||||
(
|
||||
"BBAA99887766554433221109",
|
||||
"",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617",
|
||||
"221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3C"
|
||||
"E725F32494B9F914D85C0B1EB38357FF"
|
||||
),
|
||||
(
|
||||
"BBAA9988776655443322110A",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F",
|
||||
"BD6F6C496201C69296C11EFD138A467ABD3C707924B964DE"
|
||||
"AFFC40319AF5A48540FBBA186C5553C68AD9F592A79A4240"
|
||||
),
|
||||
(
|
||||
"BBAA9988776655443322110B",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F",
|
||||
"",
|
||||
"FE80690BEE8A485D11F32965BC9D2A32"
|
||||
),
|
||||
(
|
||||
"BBAA9988776655443322110C",
|
||||
"",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F",
|
||||
"2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF4"
|
||||
"6040C53F1432BCDFB5E1DDE3BC18A5F840B52E653444D5DF"
|
||||
),
|
||||
(
|
||||
"BBAA9988776655443322110D",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F2021222324252627",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F2021222324252627",
|
||||
"D5CA91748410C1751FF8A2F618255B68A0A12E093FF45460"
|
||||
"6E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483"
|
||||
"A7035490C5769E60"
|
||||
),
|
||||
(
|
||||
"BBAA9988776655443322110E",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F2021222324252627",
|
||||
"",
|
||||
"C5CD9D1850C141E358649994EE701B68"
|
||||
),
|
||||
(
|
||||
"BBAA9988776655443322110F",
|
||||
"",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F2021222324252627",
|
||||
"4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15"
|
||||
"A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95"
|
||||
"A98CA5F3000B1479"
|
||||
)
|
||||
)
|
||||
|
||||
# Tuple with
|
||||
# - key
|
||||
# - nonce
|
||||
# - authenticated data
|
||||
# - plaintext
|
||||
# - ciphertext and 12 byte MAC tag
|
||||
tv2 = (
|
||||
"0F0E0D0C0B0A09080706050403020100",
|
||||
"BBAA9988776655443322110D",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F2021222324252627",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F2021222324252627",
|
||||
"1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1"
|
||||
"A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FD"
|
||||
"AC4F02AA"
|
||||
)
|
||||
|
||||
# Tuple with
|
||||
# - key length
|
||||
# - MAC tag length
|
||||
# - Expected output
|
||||
tv3 = (
|
||||
(128, 128, "67E944D23256C5E0B6C61FA22FDF1EA2"),
|
||||
(192, 128, "F673F2C3E7174AAE7BAE986CA9F29E17"),
|
||||
(256, 128, "D90EB8E9C977C88B79DD793D7FFA161C"),
|
||||
(128, 96, "77A3D8E73589158D25D01209"),
|
||||
(192, 96, "05D56EAD2752C86BE6932C5E"),
|
||||
(256, 96, "5458359AC23B0CBA9E6330DD"),
|
||||
(128, 64, "192C9B7BD90BA06A"),
|
||||
(192, 64, "0066BC6E0EF34E24"),
|
||||
(256, 64, "7D4EA5D445501CBE"),
|
||||
)
|
||||
|
||||
def test1(self):
|
||||
key = unhexlify(b(self.tv1_key))
|
||||
for tv in self.tv1:
|
||||
nonce, aad, pt, ct = [unhexlify(b(x)) for x in tv]
|
||||
ct, mac_tag = ct[:-16], ct[-16:]
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OCB, nonce=nonce)
|
||||
cipher.update(aad)
|
||||
ct2 = cipher.encrypt(pt) + cipher.encrypt()
|
||||
self.assertEqual(ct, ct2)
|
||||
self.assertEqual(mac_tag, cipher.digest())
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OCB, nonce=nonce)
|
||||
cipher.update(aad)
|
||||
pt2 = cipher.decrypt(ct) + cipher.decrypt()
|
||||
self.assertEqual(pt, pt2)
|
||||
cipher.verify(mac_tag)
|
||||
|
||||
def test2(self):
|
||||
|
||||
key, nonce, aad, pt, ct = [unhexlify(b(x)) for x in self.tv2]
|
||||
ct, mac_tag = ct[:-12], ct[-12:]
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OCB, nonce=nonce, mac_len=12)
|
||||
cipher.update(aad)
|
||||
ct2 = cipher.encrypt(pt) + cipher.encrypt()
|
||||
self.assertEqual(ct, ct2)
|
||||
self.assertEqual(mac_tag, cipher.digest())
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OCB, nonce=nonce, mac_len=12)
|
||||
cipher.update(aad)
|
||||
pt2 = cipher.decrypt(ct) + cipher.decrypt()
|
||||
self.assertEqual(pt, pt2)
|
||||
cipher.verify(mac_tag)
|
||||
|
||||
def test3(self):
|
||||
for keylen, taglen, result in self.tv3:
|
||||
result2 = algo_rfc7253(keylen, taglen, 96)
|
||||
self.assertEqual(unhexlify(b(result)), result2)
|
||||
|
||||
|
||||
class OcbDkgTest(unittest.TestCase):
|
||||
"""Test vectors from https://gitlab.com/dkg/ocb-test-vectors"""
|
||||
|
||||
def test_1_2(self):
|
||||
tvs = []
|
||||
for fi in (1, 2):
|
||||
for nb in (104, 112, 120):
|
||||
tv_file = load_test_vectors(("Cipher", "AES"),
|
||||
"test-vector-%d-nonce%d.txt" % (fi, nb),
|
||||
"DKG tests, %d, %d bits" % (fi, nb),
|
||||
{})
|
||||
if tv_file is None:
|
||||
break
|
||||
key = tv_file[0].k
|
||||
for tv in tv_file[1:]:
|
||||
tv.k = key
|
||||
tvs.append(tv)
|
||||
|
||||
for tv in tvs:
|
||||
k, n, a, p, c = tv.k, tv.n, tv.a, tv.p, tv.c
|
||||
mac_len = len(c) - len(p)
|
||||
cipher = AES.new(k, AES.MODE_OCB, nonce=n, mac_len=mac_len)
|
||||
cipher.update(a)
|
||||
c_out, tag_out = cipher.encrypt_and_digest(p)
|
||||
self.assertEqual(c, c_out + tag_out)
|
||||
|
||||
def test_3(self):
|
||||
|
||||
def check(keylen, taglen, noncelen, exp):
|
||||
result = algo_rfc7253(keylen, taglen, noncelen)
|
||||
self.assertEqual(result, unhexlify(exp))
|
||||
|
||||
# test-vector-3-nonce104.txt
|
||||
check(128, 128, 104, "C47F5F0341E15326D4D1C46F47F05062")
|
||||
check(192, 128, 104, "95B9167A38EB80495DFC561A8486E109")
|
||||
check(256, 128, 104, "AFE1CDDB97028FD92F8FB3C8CFBA7D83")
|
||||
check(128, 96, 104, "F471B4983BA80946DF217A54")
|
||||
check(192, 96, 104, "5AE828BC51C24D85FA5CC7B2")
|
||||
check(256, 96, 104, "8C8335982E2B734616CAD14C")
|
||||
check(128, 64, 104, "B553F74B85FD1E5B")
|
||||
check(192, 64, 104, "3B49D20E513531F9")
|
||||
check(256, 64, 104, "ED6DA5B1216BF8BB")
|
||||
|
||||
# test-vector-3-nonce112.txt
|
||||
check(128, 128, 112, "CA8AFCA031BAC3F480A583BD6C50A547")
|
||||
check(192, 128, 112, "D170C1DF356308079DA9A3F619147148")
|
||||
check(256, 128, 112, "57F94381F2F9231EFB04AECD323757C3")
|
||||
check(128, 96, 112, "3A618B2531ED39F260C750DC")
|
||||
check(192, 96, 112, "9071EB89FEDBADDA88FD286E")
|
||||
check(256, 96, 112, "FDF0EFB97F21A39AC4BAB5AC")
|
||||
check(128, 64, 112, "FAB2FF3A8DD82A13")
|
||||
check(192, 64, 112, "AC01D912BD0737D3")
|
||||
check(256, 64, 112, "9D1FD0B500EA4ECF")
|
||||
|
||||
# test-vector-3-nonce120.txt
|
||||
check(128, 128, 120, "9E043A7140A25FB91F43BCC9DD7E0F46")
|
||||
check(192, 128, 120, "680000E53908323A7F396B955B8EC641")
|
||||
check(256, 128, 120, "8304B97FAACDA56E676602E1878A7E6F")
|
||||
check(128, 96, 120, "81F978AC9867E825D339847D")
|
||||
check(192, 96, 120, "EFCF2D60B24926ADA48CF5B1")
|
||||
check(256, 96, 120, "84961DC56E917B165E58C174")
|
||||
check(128, 64, 120, "227AEE6C9D905A61")
|
||||
check(192, 64, 120, "541DE691B9E1A2F9")
|
||||
check(256, 64, 120, "B0E761381C7129FC")
|
||||
|
||||
def test_2_bugfix(self):
|
||||
nonce = unhexlify("EEDDCCBBAA9988776655443322110D")
|
||||
key = unhexlify("0F0E0D0C0B0A09080706050403020100")
|
||||
A = unhexlify("000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F2021222324252627")
|
||||
P = unhexlify("000102030405060708090A0B0C0D0E0F1011121314151617"
|
||||
"18191A1B1C1D1E1F2021222324252627")
|
||||
C = unhexlify("07E903BFC49552411ABC865F5ECE60F6FAD1F5A9F14D3070"
|
||||
"FA2F1308A563207FFE14C1EEA44B22059C7484319D8A2C53"
|
||||
"C236A7B3")
|
||||
mac_len = len(C) - len(P)
|
||||
|
||||
# Prior to version 3.17, a nonce of maximum length (15 bytes)
|
||||
# was actually used as a 14 byte nonce. The last byte was erroneously
|
||||
# ignored.
|
||||
buggy_result = unhexlify("BA015C4E5AE54D76C890AE81BD40DC57"
|
||||
"03EDC30E8AC2A58BC5D8FA4D61C5BAE6"
|
||||
"C39BEAC435B2FD56A2A5085C1B135D77"
|
||||
"0C8264B7")
|
||||
cipher = AES.new(key, AES.MODE_OCB, nonce=nonce[:-1], mac_len=mac_len)
|
||||
cipher.update(A)
|
||||
C_out2, tag_out2 = cipher.encrypt_and_digest(P)
|
||||
self.assertEqual(buggy_result, C_out2 + tag_out2)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(OcbTests)
|
||||
tests += list_test_cases(OcbFSMTests)
|
||||
tests += list_test_cases(OcbRfc7253Test)
|
||||
tests += list_test_cases(OcbDkgTest)
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def suite():
|
||||
return unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,238 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.Util.py3compat import tobytes
|
||||
from Crypto.Cipher import AES, DES3, DES
|
||||
from Crypto.Hash import SHAKE128
|
||||
from Crypto.SelfTest.loader import load_test_vectors_wycheproof
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
from Crypto.SelfTest.Cipher.test_CBC import BlockChainingTests
|
||||
|
||||
class OfbTests(BlockChainingTests):
|
||||
|
||||
aes_mode = AES.MODE_OFB
|
||||
des3_mode = DES3.MODE_OFB
|
||||
|
||||
# Redefine test_unaligned_data_128/64
|
||||
|
||||
def test_unaligned_data_128(self):
|
||||
plaintexts = [ b"7777777" ] * 100
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=8)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=8)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=128)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=128)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
def test_unaligned_data_64(self):
|
||||
plaintexts = [ b"7777777" ] * 100
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=8)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=8)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=64)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=64)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
|
||||
from Crypto.SelfTest.Cipher.test_CBC import NistBlockChainingVectors
|
||||
|
||||
class NistOfbVectors(NistBlockChainingVectors):
|
||||
aes_mode = AES.MODE_OFB
|
||||
des_mode = DES.MODE_OFB
|
||||
des3_mode = DES3.MODE_OFB
|
||||
|
||||
|
||||
# Create one test method per file
|
||||
nist_aes_kat_mmt_files = (
|
||||
# KAT
|
||||
"OFBGFSbox128.rsp",
|
||||
"OFBGFSbox192.rsp",
|
||||
"OFBGFSbox256.rsp",
|
||||
"OFBKeySbox128.rsp",
|
||||
"OFBKeySbox192.rsp",
|
||||
"OFBKeySbox256.rsp",
|
||||
"OFBVarKey128.rsp",
|
||||
"OFBVarKey192.rsp",
|
||||
"OFBVarKey256.rsp",
|
||||
"OFBVarTxt128.rsp",
|
||||
"OFBVarTxt192.rsp",
|
||||
"OFBVarTxt256.rsp",
|
||||
# MMT
|
||||
"OFBMMT128.rsp",
|
||||
"OFBMMT192.rsp",
|
||||
"OFBMMT256.rsp",
|
||||
)
|
||||
nist_aes_mct_files = (
|
||||
"OFBMCT128.rsp",
|
||||
"OFBMCT192.rsp",
|
||||
"OFBMCT256.rsp",
|
||||
)
|
||||
|
||||
for file_name in nist_aes_kat_mmt_files:
|
||||
def new_func(self, file_name=file_name):
|
||||
self._do_kat_aes_test(file_name)
|
||||
setattr(NistOfbVectors, "test_AES_" + file_name, new_func)
|
||||
|
||||
for file_name in nist_aes_mct_files:
|
||||
def new_func(self, file_name=file_name):
|
||||
self._do_mct_aes_test(file_name)
|
||||
setattr(NistOfbVectors, "test_AES_" + file_name, new_func)
|
||||
del file_name, new_func
|
||||
|
||||
nist_tdes_files = (
|
||||
"TOFBMMT2.rsp", # 2TDES
|
||||
"TOFBMMT3.rsp", # 3TDES
|
||||
"TOFBinvperm.rsp", # Single DES
|
||||
"TOFBpermop.rsp",
|
||||
"TOFBsubtab.rsp",
|
||||
"TOFBvarkey.rsp",
|
||||
"TOFBvartext.rsp",
|
||||
)
|
||||
|
||||
for file_name in nist_tdes_files:
|
||||
def new_func(self, file_name=file_name):
|
||||
self._do_tdes_test(file_name)
|
||||
setattr(NistOfbVectors, "test_TDES_" + file_name, new_func)
|
||||
|
||||
# END OF NIST OFB TEST VECTORS
|
||||
|
||||
|
||||
class SP800TestVectors(unittest.TestCase):
|
||||
"""Class exercising the OFB test vectors found in Section F.4
|
||||
of NIST SP 800-3A"""
|
||||
|
||||
def test_aes_128(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = '3b3fd92eb72dad20333449f8e83cfb4a' +\
|
||||
'7789508d16918f03f53c52dac54ed825' +\
|
||||
'9740051e9c5fecf64344f7a82260edcc' +\
|
||||
'304c6528f659c77866a510d9c1d6ae5e'
|
||||
key = '2b7e151628aed2a6abf7158809cf4f3c'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.encrypt(plaintext[:-8]), ciphertext[:-8])
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.decrypt(ciphertext[:-8]), plaintext[:-8])
|
||||
|
||||
def test_aes_192(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = 'cdc80d6fddf18cab34c25909c99a4174' +\
|
||||
'fcc28b8d4c63837c09e81700c1100401' +\
|
||||
'8d9a9aeac0f6596f559c6d4daf59a5f2' +\
|
||||
'6d9f200857ca6c3e9cac524bd9acc92a'
|
||||
key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.encrypt(plaintext[:-8]), ciphertext[:-8])
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.decrypt(ciphertext[:-8]), plaintext[:-8])
|
||||
|
||||
def test_aes_256(self):
|
||||
plaintext = '6bc1bee22e409f96e93d7e117393172a' +\
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51' +\
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef' +\
|
||||
'f69f2445df4f9b17ad2b417be66c3710'
|
||||
ciphertext = 'dc7e84bfda79164b7ecd8486985d3860' +\
|
||||
'4febdc6740d20b3ac88f6ad82a4fb08d' +\
|
||||
'71ab47a086e86eedf39d1c5bba97c408' +\
|
||||
'0126141d67f37be8538f5a8be740e484'
|
||||
key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4'
|
||||
iv = '000102030405060708090a0b0c0d0e0f'
|
||||
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.encrypt(plaintext), ciphertext)
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.decrypt(ciphertext), plaintext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.encrypt(plaintext[:-8]), ciphertext[:-8])
|
||||
cipher = AES.new(key, AES.MODE_OFB, iv)
|
||||
self.assertEqual(cipher.decrypt(ciphertext[:-8]), plaintext[:-8])
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(OfbTests)
|
||||
if config.get('slow_tests'):
|
||||
tests += list_test_cases(NistOfbVectors)
|
||||
tests += list_test_cases(SP800TestVectors)
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,218 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.Util.py3compat import tobytes
|
||||
from Crypto.Cipher import AES, DES3, DES
|
||||
from Crypto.Hash import SHAKE128
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
|
||||
from Crypto.SelfTest.Cipher.test_CBC import BlockChainingTests
|
||||
|
||||
class OpenPGPTests(BlockChainingTests):
|
||||
|
||||
aes_mode = AES.MODE_OPENPGP
|
||||
des3_mode = DES3.MODE_OPENPGP
|
||||
|
||||
# Redefine test_unaligned_data_128/64
|
||||
|
||||
key_128 = get_tag_random("key_128", 16)
|
||||
key_192 = get_tag_random("key_192", 24)
|
||||
iv_128 = get_tag_random("iv_128", 16)
|
||||
iv_64 = get_tag_random("iv_64", 8)
|
||||
data_128 = get_tag_random("data_128", 16)
|
||||
|
||||
def test_loopback_128(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128)
|
||||
pt = get_tag_random("plaintext", 16 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
eiv, ct = ct[:18], ct[18:]
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OPENPGP, eiv)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_loopback_64(self):
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_OPENPGP, self.iv_64)
|
||||
pt = get_tag_random("plaintext", 8 * 100)
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
eiv, ct = ct[:10], ct[10:]
|
||||
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_OPENPGP, eiv)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_IV_iv_attributes(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128)
|
||||
eiv = cipher.encrypt(b"")
|
||||
self.assertEqual(cipher.iv, self.iv_128)
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OPENPGP, eiv)
|
||||
self.assertEqual(cipher.iv, self.iv_128)
|
||||
|
||||
def test_null_encryption_decryption(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128)
|
||||
eiv = cipher.encrypt(b"")
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OPENPGP, eiv)
|
||||
self.assertEqual(cipher.decrypt(b""), b"")
|
||||
|
||||
def test_either_encrypt_or_decrypt(self):
|
||||
cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128)
|
||||
eiv = cipher.encrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.decrypt, b"")
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OPENPGP, eiv)
|
||||
cipher.decrypt(b"")
|
||||
self.assertRaises(TypeError, cipher.encrypt, b"")
|
||||
|
||||
def test_unaligned_data_128(self):
|
||||
plaintexts = [ b"7777777" ] * 100
|
||||
|
||||
cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
def test_unaligned_data_64(self):
|
||||
plaintexts = [ b"7777777" ] * 100
|
||||
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_OPENPGP, self.iv_64)
|
||||
ciphertexts = [ cipher.encrypt(x) for x in plaintexts ]
|
||||
cipher = DES3.new(self.key_192, DES3.MODE_OPENPGP, self.iv_64)
|
||||
self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
|
||||
|
||||
def test_output_param(self):
|
||||
pass
|
||||
|
||||
def test_output_param_same_buffer(self):
|
||||
pass
|
||||
|
||||
def test_output_param_memoryview(self):
|
||||
pass
|
||||
|
||||
def test_output_param_neg(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestVectors(unittest.TestCase):
|
||||
|
||||
def test_aes(self):
|
||||
# The following test vectors have been generated with gpg v1.4.0.
|
||||
# The command line used was:
|
||||
#
|
||||
# gpg -c -z 0 --cipher-algo AES --passphrase secret_passphrase \
|
||||
# --disable-mdc --s2k-mode 0 --output ct pt
|
||||
#
|
||||
# As result, the content of the file 'pt' is encrypted with a key derived
|
||||
# from 'secret_passphrase' and written to file 'ct'.
|
||||
# Test vectors must be extracted from 'ct', which is a collection of
|
||||
# TLVs (see RFC4880 for all details):
|
||||
# - the encrypted data (with the encrypted IV as prefix) is the payload
|
||||
# of the TLV with tag 9 (Symmetrical Encrypted Data Packet).
|
||||
# This is the ciphertext in the test vector.
|
||||
# - inside the encrypted part, there is a further layer of TLVs. One must
|
||||
# look for tag 11 (Literal Data Packet); in its payload, after a short
|
||||
# but time dependent header, there is the content of file 'pt'.
|
||||
# In the test vector, the plaintext is the complete set of TLVs that gets
|
||||
# encrypted. It is not just the content of 'pt'.
|
||||
# - the key is the leftmost 16 bytes of the SHA1 digest of the password.
|
||||
# The test vector contains such shortened digest.
|
||||
#
|
||||
# Note that encryption uses a clear IV, and decryption an encrypted IV
|
||||
|
||||
plaintext = 'ac18620270744fb4f647426c61636b4361745768697465436174'
|
||||
ciphertext = 'dc6b9e1f095de609765c59983db5956ae4f63aea7405389d2ebb'
|
||||
key = '5baa61e4c9b93f3f0682250b6cf8331b'
|
||||
iv = '3d7d3e62282add7eb203eeba5c800733'
|
||||
encrypted_iv='fd934601ef49cb58b6d9aebca6056bdb96ef'
|
||||
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
encrypted_iv = unhexlify(encrypted_iv)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OPENPGP, iv)
|
||||
ct = cipher.encrypt(plaintext)
|
||||
self.assertEqual(ct[:18], encrypted_iv)
|
||||
self.assertEqual(ct[18:], ciphertext)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_OPENPGP, encrypted_iv)
|
||||
pt = cipher.decrypt(ciphertext)
|
||||
self.assertEqual(pt, plaintext)
|
||||
|
||||
def test_des3(self):
|
||||
# The following test vectors have been generated with gpg v1.4.0.
|
||||
# The command line used was:
|
||||
# gpg -c -z 0 --cipher-algo 3DES --passphrase secret_passphrase \
|
||||
# --disable-mdc --s2k-mode 0 --output ct pt
|
||||
# For an explanation, see test_AES.py .
|
||||
|
||||
plaintext = 'ac1762037074324fb53ba3596f73656d69746556616c6c6579'
|
||||
ciphertext = '9979238528357b90e2e0be549cb0b2d5999b9a4a447e5c5c7d'
|
||||
key = '7ade65b460f5ea9be35f9e14aa883a2048e3824aa616c0b2'
|
||||
iv='cd47e2afb8b7e4b0'
|
||||
encrypted_iv='6a7eef0b58050e8b904a'
|
||||
|
||||
plaintext = unhexlify(plaintext)
|
||||
ciphertext = unhexlify(ciphertext)
|
||||
key = unhexlify(key)
|
||||
iv = unhexlify(iv)
|
||||
encrypted_iv = unhexlify(encrypted_iv)
|
||||
|
||||
cipher = DES3.new(key, DES3.MODE_OPENPGP, iv)
|
||||
ct = cipher.encrypt(plaintext)
|
||||
self.assertEqual(ct[:10], encrypted_iv)
|
||||
self.assertEqual(ct[10:], ciphertext)
|
||||
|
||||
cipher = DES3.new(key, DES3.MODE_OPENPGP, encrypted_iv)
|
||||
pt = cipher.decrypt(ciphertext)
|
||||
self.assertEqual(pt, plaintext)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(OpenPGPTests)
|
||||
tests += list_test_cases(TestVectors)
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,552 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import json
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.SelfTest.loader import load_test_vectors_wycheproof
|
||||
|
||||
from Crypto.Util.py3compat import tobytes, bchr
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Hash import SHAKE128
|
||||
|
||||
from Crypto.Util.strxor import strxor
|
||||
|
||||
|
||||
def get_tag_random(tag, length):
|
||||
return SHAKE128.new(data=tobytes(tag)).read(length)
|
||||
|
||||
|
||||
class SivTests(unittest.TestCase):
|
||||
|
||||
key_256 = get_tag_random("key_256", 32)
|
||||
key_384 = get_tag_random("key_384", 48)
|
||||
key_512 = get_tag_random("key_512", 64)
|
||||
nonce_96 = get_tag_random("nonce_128", 12)
|
||||
data = get_tag_random("data", 128)
|
||||
|
||||
def test_loopback_128(self):
|
||||
for key in self.key_256, self.key_384, self.key_512:
|
||||
cipher = AES.new(key, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
pt = get_tag_random("plaintext", 16 * 100)
|
||||
ct, mac = cipher.encrypt_and_digest(pt)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
pt2 = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_nonce(self):
|
||||
# Deterministic encryption
|
||||
AES.new(self.key_256, AES.MODE_SIV)
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, self.nonce_96)
|
||||
ct1, tag1 = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
ct2, tag2 = cipher.encrypt_and_digest(self.data)
|
||||
self.assertEqual(ct1 + tag1, ct2 + tag2)
|
||||
|
||||
def test_nonce_must_be_bytes(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_256, AES.MODE_SIV,
|
||||
nonce=u'test12345678')
|
||||
|
||||
def test_nonce_length(self):
|
||||
# nonce can be of any length (but not empty)
|
||||
self.assertRaises(ValueError, AES.new, self.key_256, AES.MODE_SIV,
|
||||
nonce=b"")
|
||||
|
||||
for x in range(1, 128):
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=bchr(1) * x)
|
||||
cipher.encrypt_and_digest(b'\x01')
|
||||
|
||||
def test_block_size_128(self):
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.block_size, AES.block_size)
|
||||
|
||||
def test_nonce_attribute(self):
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
self.assertEqual(cipher.nonce, self.nonce_96)
|
||||
|
||||
# By default, no nonce is randomly generated
|
||||
self.assertFalse(hasattr(AES.new(self.key_256, AES.MODE_SIV), "nonce"))
|
||||
|
||||
def test_unknown_parameters(self):
|
||||
self.assertRaises(TypeError, AES.new, self.key_256, AES.MODE_SIV,
|
||||
self.nonce_96, 7)
|
||||
self.assertRaises(TypeError, AES.new, self.key_256, AES.MODE_SIV,
|
||||
nonce=self.nonce_96, unknown=7)
|
||||
|
||||
# But some are only known by the base cipher
|
||||
# (e.g. use_aesni consumed by the AES module)
|
||||
AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96,
|
||||
use_aesni=False)
|
||||
|
||||
def test_encrypt_excludes_decrypt(self):
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.encrypt_and_digest(self.data)
|
||||
self.assertRaises(TypeError, cipher.decrypt, self.data)
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.encrypt_and_digest(self.data)
|
||||
self.assertRaises(TypeError, cipher.decrypt_and_verify,
|
||||
self.data, self.data)
|
||||
|
||||
def test_data_must_be_bytes(self):
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*')
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt_and_verify,
|
||||
u'test1234567890-*', b"xxxx")
|
||||
|
||||
def test_mac_len(self):
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
_, mac = cipher.encrypt_and_digest(self.data)
|
||||
self.assertEqual(len(mac), 16)
|
||||
|
||||
def test_invalid_mac(self):
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
invalid_mac = strxor_c(mac, 0x01)
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct,
|
||||
invalid_mac)
|
||||
|
||||
def test_hex_mac(self):
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
mac_hex = cipher.hexdigest()
|
||||
self.assertEqual(cipher.digest(), unhexlify(mac_hex))
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.hexverify(mac_hex)
|
||||
|
||||
def test_bytearray(self):
|
||||
|
||||
# Encrypt
|
||||
key = bytearray(self.key_256)
|
||||
nonce = bytearray(self.nonce_96)
|
||||
data = bytearray(self.data)
|
||||
header = bytearray(self.data)
|
||||
|
||||
cipher1 = AES.new(self.key_256,
|
||||
AES.MODE_SIV,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data)
|
||||
ct, tag = cipher1.encrypt_and_digest(self.data)
|
||||
|
||||
cipher2 = AES.new(key,
|
||||
AES.MODE_SIV,
|
||||
nonce=nonce)
|
||||
key[:3] = b'\xFF\xFF\xFF'
|
||||
nonce[:3] = b'\xFF\xFF\xFF'
|
||||
cipher2.update(header)
|
||||
header[:3] = b'\xFF\xFF\xFF'
|
||||
ct_test, tag_test = cipher2.encrypt_and_digest(data)
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key = bytearray(self.key_256)
|
||||
nonce = bytearray(self.nonce_96)
|
||||
header = bytearray(self.data)
|
||||
ct_ba = bytearray(ct)
|
||||
tag_ba = bytearray(tag)
|
||||
|
||||
cipher3 = AES.new(key,
|
||||
AES.MODE_SIV,
|
||||
nonce=nonce)
|
||||
key[:3] = b'\xFF\xFF\xFF'
|
||||
nonce[:3] = b'\xFF\xFF\xFF'
|
||||
cipher3.update(header)
|
||||
header[:3] = b'\xFF\xFF\xFF'
|
||||
pt_test = cipher3.decrypt_and_verify(ct_ba, tag_ba)
|
||||
|
||||
self.assertEqual(self.data, pt_test)
|
||||
|
||||
def test_memoryview(self):
|
||||
|
||||
# Encrypt
|
||||
key = memoryview(bytearray(self.key_256))
|
||||
nonce = memoryview(bytearray(self.nonce_96))
|
||||
data = memoryview(bytearray(self.data))
|
||||
header = memoryview(bytearray(self.data))
|
||||
|
||||
cipher1 = AES.new(self.key_256,
|
||||
AES.MODE_SIV,
|
||||
nonce=self.nonce_96)
|
||||
cipher1.update(self.data)
|
||||
ct, tag = cipher1.encrypt_and_digest(self.data)
|
||||
|
||||
cipher2 = AES.new(key,
|
||||
AES.MODE_SIV,
|
||||
nonce=nonce)
|
||||
key[:3] = b'\xFF\xFF\xFF'
|
||||
nonce[:3] = b'\xFF\xFF\xFF'
|
||||
cipher2.update(header)
|
||||
header[:3] = b'\xFF\xFF\xFF'
|
||||
ct_test, tag_test= cipher2.encrypt_and_digest(data)
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(tag, tag_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decrypt
|
||||
key = memoryview(bytearray(self.key_256))
|
||||
nonce = memoryview(bytearray(self.nonce_96))
|
||||
header = memoryview(bytearray(self.data))
|
||||
ct_ba = memoryview(bytearray(ct))
|
||||
tag_ba = memoryview(bytearray(tag))
|
||||
|
||||
cipher3 = AES.new(key,
|
||||
AES.MODE_SIV,
|
||||
nonce=nonce)
|
||||
key[:3] = b'\xFF\xFF\xFF'
|
||||
nonce[:3] = b'\xFF\xFF\xFF'
|
||||
cipher3.update(header)
|
||||
header[:3] = b'\xFF\xFF\xFF'
|
||||
pt_test = cipher3.decrypt_and_verify(ct_ba, tag_ba)
|
||||
|
||||
self.assertEqual(self.data, pt_test)
|
||||
|
||||
def test_output_param(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
ct, tag = cipher.encrypt_and_digest(pt)
|
||||
|
||||
output = bytearray(128)
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
res, tag_out = cipher.encrypt_and_digest(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
self.assertEqual(tag, tag_out)
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
res = cipher.decrypt_and_verify(ct, tag, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
def test_output_param_memoryview(self):
|
||||
|
||||
pt = b'5' * 128
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
ct, tag = cipher.encrypt_and_digest(pt)
|
||||
|
||||
output = memoryview(bytearray(128))
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.encrypt_and_digest(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.decrypt_and_verify(ct, tag, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
def test_output_param_neg(self):
|
||||
LEN_PT = 128
|
||||
|
||||
pt = b'5' * LEN_PT
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
ct, tag = cipher.encrypt_and_digest(pt)
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt_and_digest, pt, output=b'0' * LEN_PT)
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt_and_verify, ct, tag, output=b'0' * LEN_PT)
|
||||
|
||||
shorter_output = bytearray(LEN_PT - 1)
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.encrypt_and_digest, pt, output=shorter_output)
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
self.assertRaises(ValueError, cipher.decrypt_and_verify, ct, tag, output=shorter_output)
|
||||
|
||||
|
||||
class SivFSMTests(unittest.TestCase):
|
||||
|
||||
key_256 = get_tag_random("key_256", 32)
|
||||
nonce_96 = get_tag_random("nonce_96", 12)
|
||||
data = get_tag_random("data", 128)
|
||||
|
||||
def test_invalid_init_encrypt(self):
|
||||
# Path INIT->ENCRYPT fails
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV,
|
||||
nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.encrypt, b"xxx")
|
||||
|
||||
def test_invalid_init_decrypt(self):
|
||||
# Path INIT->DECRYPT fails
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV,
|
||||
nonce=self.nonce_96)
|
||||
self.assertRaises(TypeError, cipher.decrypt, b"xxx")
|
||||
|
||||
def test_valid_init_update_digest_verify(self):
|
||||
# No plaintext, fixed authenticated data
|
||||
# Verify path INIT->UPDATE->DIGEST
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
mac = cipher.digest()
|
||||
|
||||
# Verify path INIT->UPDATE->VERIFY
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV,
|
||||
nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_init_digest(self):
|
||||
# Verify path INIT->DIGEST
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.digest()
|
||||
|
||||
def test_valid_init_verify(self):
|
||||
# Verify path INIT->VERIFY
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
mac = cipher.digest()
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.verify(mac)
|
||||
|
||||
def test_valid_multiple_digest_or_verify(self):
|
||||
# Multiple calls to digest
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
first_mac = cipher.digest()
|
||||
for x in range(4):
|
||||
self.assertEqual(first_mac, cipher.digest())
|
||||
|
||||
# Multiple calls to verify
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
for x in range(5):
|
||||
cipher.verify(first_mac)
|
||||
|
||||
def test_valid_encrypt_and_digest_decrypt_and_verify(self):
|
||||
# encrypt_and_digest
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
ct, mac = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
# decrypt_and_verify
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.update(self.data)
|
||||
pt = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(self.data, pt)
|
||||
|
||||
def test_invalid_multiple_encrypt_and_digest(self):
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
ct, tag = cipher.encrypt_and_digest(self.data)
|
||||
self.assertRaises(TypeError, cipher.encrypt_and_digest, b'')
|
||||
|
||||
def test_invalid_multiple_decrypt_and_verify(self):
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
ct, tag = cipher.encrypt_and_digest(self.data)
|
||||
|
||||
cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96)
|
||||
cipher.decrypt_and_verify(ct, tag)
|
||||
self.assertRaises(TypeError, cipher.decrypt_and_verify, ct, tag)
|
||||
|
||||
|
||||
def transform(tv):
|
||||
new_tv = [[unhexlify(x) for x in tv[0].split("-")]]
|
||||
new_tv += [ unhexlify(x) for x in tv[1:5]]
|
||||
if tv[5]:
|
||||
nonce = unhexlify(tv[5])
|
||||
else:
|
||||
nonce = None
|
||||
new_tv += [ nonce ]
|
||||
return new_tv
|
||||
|
||||
|
||||
class TestVectors(unittest.TestCase):
|
||||
"""Class exercising the SIV test vectors found in RFC5297"""
|
||||
|
||||
# This is a list of tuples with 5 items:
|
||||
#
|
||||
# 1. Header + '|' + plaintext
|
||||
# 2. Header + '|' + ciphertext + '|' + MAC
|
||||
# 3. AES-128 key
|
||||
# 4. Description
|
||||
# 5. Dictionary of parameters to be passed to AES.new().
|
||||
# It must include the nonce.
|
||||
#
|
||||
# A "Header" is a dash ('-') separated sequece of components.
|
||||
#
|
||||
test_vectors_hex = [
|
||||
(
|
||||
'101112131415161718191a1b1c1d1e1f2021222324252627',
|
||||
'112233445566778899aabbccddee',
|
||||
'40c02b9690c4dc04daef7f6afe5c',
|
||||
'85632d07c6e8f37f950acd320a2ecc93',
|
||||
'fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff',
|
||||
None
|
||||
),
|
||||
(
|
||||
'00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddccbbaa9988' +
|
||||
'7766554433221100-102030405060708090a0',
|
||||
'7468697320697320736f6d6520706c61696e7465787420746f20656e63727970' +
|
||||
'74207573696e67205349562d414553',
|
||||
'cb900f2fddbe404326601965c889bf17dba77ceb094fa663b7a3f748ba8af829' +
|
||||
'ea64ad544a272e9c485b62a3fd5c0d',
|
||||
'7bdb6e3b432667eb06f4d14bff2fbd0f',
|
||||
'7f7e7d7c7b7a79787776757473727170404142434445464748494a4b4c4d4e4f',
|
||||
'09f911029d74e35bd84156c5635688c0'
|
||||
),
|
||||
]
|
||||
|
||||
test_vectors = [ transform(tv) for tv in test_vectors_hex ]
|
||||
|
||||
def runTest(self):
|
||||
for assoc_data, pt, ct, mac, key, nonce in self.test_vectors:
|
||||
|
||||
# Encrypt
|
||||
cipher = AES.new(key, AES.MODE_SIV, nonce=nonce)
|
||||
for x in assoc_data:
|
||||
cipher.update(x)
|
||||
ct2, mac2 = cipher.encrypt_and_digest(pt)
|
||||
self.assertEqual(ct, ct2)
|
||||
self.assertEqual(mac, mac2)
|
||||
|
||||
# Decrypt
|
||||
cipher = AES.new(key, AES.MODE_SIV, nonce=nonce)
|
||||
for x in assoc_data:
|
||||
cipher.update(x)
|
||||
pt2 = cipher.decrypt_and_verify(ct, mac)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
|
||||
class TestVectorsWycheproof(unittest.TestCase):
|
||||
|
||||
def __init__(self):
|
||||
unittest.TestCase.__init__(self)
|
||||
self._id = "None"
|
||||
|
||||
def setUp(self):
|
||||
self.tv = load_test_vectors_wycheproof(("Cipher", "wycheproof"),
|
||||
"aes_siv_cmac_test.json",
|
||||
"Wycheproof AES SIV")
|
||||
|
||||
def shortDescription(self):
|
||||
return self._id
|
||||
|
||||
def test_encrypt(self, tv):
|
||||
self._id = "Wycheproof Encrypt AES-SIV Test #" + str(tv.id)
|
||||
|
||||
cipher = AES.new(tv.key, AES.MODE_SIV)
|
||||
cipher.update(tv.aad)
|
||||
ct, tag = cipher.encrypt_and_digest(tv.msg)
|
||||
if tv.valid:
|
||||
self.assertEqual(tag + ct, tv.ct)
|
||||
|
||||
def test_decrypt(self, tv):
|
||||
self._id = "Wycheproof Decrypt AES_SIV Test #" + str(tv.id)
|
||||
|
||||
cipher = AES.new(tv.key, AES.MODE_SIV)
|
||||
cipher.update(tv.aad)
|
||||
try:
|
||||
pt = cipher.decrypt_and_verify(tv.ct[16:], tv.ct[:16])
|
||||
except ValueError:
|
||||
assert not tv.valid
|
||||
else:
|
||||
assert tv.valid
|
||||
self.assertEqual(pt, tv.msg)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
for tv in self.tv:
|
||||
self.test_encrypt(tv)
|
||||
self.test_decrypt(tv)
|
||||
|
||||
|
||||
class TestVectorsWycheproof2(unittest.TestCase):
|
||||
|
||||
def __init__(self):
|
||||
unittest.TestCase.__init__(self)
|
||||
self._id = "None"
|
||||
|
||||
def setUp(self):
|
||||
self.tv = load_test_vectors_wycheproof(("Cipher", "wycheproof"),
|
||||
"aead_aes_siv_cmac_test.json",
|
||||
"Wycheproof AEAD SIV")
|
||||
|
||||
def shortDescription(self):
|
||||
return self._id
|
||||
|
||||
def test_encrypt(self, tv):
|
||||
self._id = "Wycheproof Encrypt AEAD-AES-SIV Test #" + str(tv.id)
|
||||
|
||||
cipher = AES.new(tv.key, AES.MODE_SIV, nonce=tv.iv)
|
||||
cipher.update(tv.aad)
|
||||
ct, tag = cipher.encrypt_and_digest(tv.msg)
|
||||
if tv.valid:
|
||||
self.assertEqual(ct, tv.ct)
|
||||
self.assertEqual(tag, tv.tag)
|
||||
|
||||
def test_decrypt(self, tv):
|
||||
self._id = "Wycheproof Decrypt AEAD-AES-SIV Test #" + str(tv.id)
|
||||
|
||||
cipher = AES.new(tv.key, AES.MODE_SIV, nonce=tv.iv)
|
||||
cipher.update(tv.aad)
|
||||
try:
|
||||
pt = cipher.decrypt_and_verify(tv.ct, tv.tag)
|
||||
except ValueError:
|
||||
assert not tv.valid
|
||||
else:
|
||||
assert tv.valid
|
||||
self.assertEqual(pt, tv.msg)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
for tv in self.tv:
|
||||
self.test_encrypt(tv)
|
||||
self.test_decrypt(tv)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
wycheproof_warnings = config.get('wycheproof_warnings')
|
||||
|
||||
tests = []
|
||||
tests += list_test_cases(SivTests)
|
||||
tests += list_test_cases(SivFSMTests)
|
||||
tests += [ TestVectors() ]
|
||||
tests += [ TestVectorsWycheproof() ]
|
||||
tests += [ TestVectorsWycheproof2() ]
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -0,0 +1,367 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/Salsa20.py: Self-test for the Salsa20 stream cipher
|
||||
#
|
||||
# Written in 2013 by Fabrizio Tarizzo <fabrizio@fabriziotarizzo.org>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.Salsa20"""
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.Util.py3compat import bchr
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
|
||||
from Crypto.Cipher import Salsa20
|
||||
|
||||
from .common import make_stream_tests
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key[, description[, params]])
|
||||
# tuples.
|
||||
test_data = [
|
||||
# Test vectors are taken from
|
||||
# http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/full/verified.test-vectors
|
||||
( '00' * 512,
|
||||
'4dfa5e481da23ea09a31022050859936da52fcee218005164f267cb65f5cfd7f'
|
||||
+ '2b4f97e0ff16924a52df269515110a07f9e460bc65ef95da58f740b7d1dbb0aa'
|
||||
+ 'd64cec189c7eb8c6bbf3d7376c80a481d43e628701f6a27afb9fe23919f24114'
|
||||
+ '8db44f70d7063efcc3dd55a0893a613c3c6fe1c127bd6f59910589293bb6ef9e'
|
||||
+ 'e24819066dee1a64f49b0bbad5988635272b169af861f85df881939f29ada6fd'
|
||||
+ '0241410e8d332ae4798d929434a2630de451ec4e0169694cbaa7ebb121ea6a2b'
|
||||
+ 'da9c1581f429e0a00f7d67e23b730676783b262e8eb43a25f55fb90b3e753aef'
|
||||
+ '8c6713ec66c51881111593ccb3e8cb8f8de124080501eeeb389c4bcb6977cf95'
|
||||
+ '7d5789631eb4554400e1e025935dfa7b3e9039d61bdc58a8697d36815bf1985c'
|
||||
+ 'efdf7ae112e5bb81e37ecf0616ce7147fc08a93a367e08631f23c03b00a8da2f'
|
||||
+ 'aa5024e5c8d30aca43fc2d5082067b21b234bc741d68fb292c6012c3764ccee3'
|
||||
+ '1e364a5403e00cfee338a21a01e7d3cefd5a770ca0ab48c435ea6116435f7ad8'
|
||||
+ '30b217b49f978a68e207ed9f462af7fb195b2115fe8f24f152e4ddc32202d6f2'
|
||||
+ 'b52fafbcfbc202d8a259a611e901d3f62d065eb13f09bbc45cd45119b843efaa'
|
||||
+ 'b375703739daced4dd4059fd71c3c47fc2f9939670fad4a46066adcc6a564578'
|
||||
+ '3308b90ffb72be04a6b147cbe38cc0c3b9267c296a92a7c69873f9f263be9703',
|
||||
'80000000000000000000000000000000',
|
||||
'128 bits key, set 1, vector 0',
|
||||
dict (iv='00'*8)),
|
||||
|
||||
( '00' * 512,
|
||||
'e3be8fdd8beca2e3ea8ef9475b29a6e7003951e1097a5c38d23b7a5fad9f6844'
|
||||
+ 'b22c97559e2723c7cbbd3fe4fc8d9a0744652a83e72a9c461876af4d7ef1a117'
|
||||
+ '8da2b74eef1b6283e7e20166abcae538e9716e4669e2816b6b20c5c356802001'
|
||||
+ 'cc1403a9a117d12a2669f456366d6ebb0f1246f1265150f793cdb4b253e348ae'
|
||||
+ '203d89bc025e802a7e0e00621d70aa36b7e07cb1e7d5b38d5e222b8b0e4b8407'
|
||||
+ '0142b1e29504767d76824850320b5368129fdd74e861b498e3be8d16f2d7d169'
|
||||
+ '57be81f47b17d9ae7c4ff15429a73e10acf250ed3a90a93c711308a74c6216a9'
|
||||
+ 'ed84cd126da7f28e8abf8bb63517e1ca98e712f4fb2e1a6aed9fdc73291faa17'
|
||||
+ '958211c4ba2ebd5838c635edb81f513a91a294e194f1c039aeec657dce40aa7e'
|
||||
+ '7c0af57cacefa40c9f14b71a4b3456a63e162ec7d8d10b8ffb1810d71001b618'
|
||||
+ '2f9f73da53b85405c11f7b2d890fa8ae0c7f2e926d8a98c7ec4e91b65120e988'
|
||||
+ '349631a700c6facec3471cb0413656e75e309456584084d7e12c5b43a41c43ed'
|
||||
+ '9a048abd9b880da65f6a665a20fe7b77cd292fe62cae644b7f7df69f32bdb331'
|
||||
+ '903e6505ce44fdc293920c6a9ec7057e23df7dad298f82ddf4efb7fdc7bfc622'
|
||||
+ '696afcfd0cddcc83c7e77f11a649d79acdc3354e9635ff137e929933a0bd6f53'
|
||||
+ '77efa105a3a4266b7c0d089d08f1e855cc32b15b93784a36e56a76cc64bc8477',
|
||||
'8000000000000000000000000000000000000000000000000000000000000000',
|
||||
'256 bits key, set 1, vector 0',
|
||||
dict (iv='00'*8)),
|
||||
|
||||
( '00' * 512,
|
||||
'169060ccb42bea7bee4d8012a02f3635eb7bca12859fa159cd559094b3507db8'
|
||||
+ '01735d1a1300102a9c9415546829cbd2021ba217b39b81d89c55b13d0c603359'
|
||||
+ '3f84159a3c84f4b4f4a0edcd9d38ff261a737909e0b66d68b5cac496f3a5be99'
|
||||
+ 'cb12c321ab711afaab36cc0947955e1a9bb952ed54425e7711279fbc81bb83f5'
|
||||
+ '6e55cea44e6daddb05858a153ea6213b3350c12aa1a83ef2726f09485fa71790'
|
||||
+ 'f9b9f922c7dda1113b1f9d56658ed3402803f511bc1f122601d5e7f0ff036e23'
|
||||
+ '23ef24bb24195b9fd574823cd8a40c29d86bd35c191e2038779ff696c712b6d8'
|
||||
+ '2e7014dbe1ac5d527af076c088c4a8d44317958189f6ef54933a7e0816b5b916'
|
||||
+ 'd8f12ed8afe9422b85e5cc9b8adec9d6cfabe8dbc1082bccc02f5a7266aa074c'
|
||||
+ 'a284e583a35837798cc0e69d4ce937653b8cdd65ce414b89138615ccb165ad19'
|
||||
+ '3c6b9c3d05eef4be921a10ea811fe61d11c6867600188e065daff90b509ec56b'
|
||||
+ 'd41e7e8968c478c78d590c2d2ee24ea009c8f49bc3d81672cfc47895a9e21c9a'
|
||||
+ '471ebf8e294bee5d2de436ac8d052bf31111b345f1da23c3a4d13b9fc5f0900a'
|
||||
+ 'a298f98f538973b8fad40d4d159777de2cfe2a3dead1645ddb49794827dba040'
|
||||
+ 'f70a0ff4ecd155e0f033604693a51e2363880e2ecf98699e7174af7c2c6b0fc6'
|
||||
+ '59ae329599a3949272a37b9b2183a0910922a3f325ae124dcbdd735364055ceb',
|
||||
'09090909090909090909090909090909',
|
||||
'128 bits key, set 2, vector 9',
|
||||
dict (iv='00'*8)),
|
||||
|
||||
( '00' * 512,
|
||||
'7041e747ceb22ed7812985465f50333124f971da1c5d6efe5ca201b886f31046'
|
||||
+ 'e757e5c3ec914f60ed1f6bce2819b6810953f12b8ba1199bf82d746a8b8a88f1'
|
||||
+ '142002978ec4c35b95dc2c82990f9e847a0ab45f2ca72625f5190c820f29f3aa'
|
||||
+ 'f5f0b5572b06b70a144f2a240c3b3098d4831fa1ce1459f8d1df226a6a79b0ab'
|
||||
+ '41e91799ef31b5ff3d756c19126b19025858ee70fbd69f2be955cb011c005e31'
|
||||
+ '32b271b378f39b0cb594e95c99ce6ff17735a541891845bbf0450afcb4a850b9'
|
||||
+ '4ee90afb713ae7e01295c74381180a3816d7020d5a396c0d97aaa783eaabb6ec'
|
||||
+ '44d5111157f2212d1b1b8fca7893e8b520cd482418c272ab119b569a2b9598eb'
|
||||
+ '355624d12e79adab81153b58cd22eaf1b2a32395dedc4a1c66f4d274070b9800'
|
||||
+ 'ea95766f0245a8295f8aadb36ddbbdfa936417c8dbc6235d19494036964d3e70'
|
||||
+ 'b125b0f800c3d53881d9d11e7970f827c2f9556935cd29e927b0aceb8cae5fd4'
|
||||
+ '0fd88a8854010a33db94c96c98735858f1c5df6844f864feaca8f41539313e7f'
|
||||
+ '3c0610214912cd5e6362197646207e2d64cd5b26c9dfe0822629dcbeb16662e8'
|
||||
+ '9ff5bf5cf2e499138a5e27bd5027329d0e68ddf53103e9e409523662e27f61f6'
|
||||
+ '5cf38c1232023e6a6ef66c315bcb2a4328642faabb7ca1e889e039e7c444b34b'
|
||||
+ 'b3443f596ac730f3df3dfcdb343c307c80f76e43e8898c5e8f43dc3bb280add0',
|
||||
'0909090909090909090909090909090909090909090909090909090909090909',
|
||||
'256 bits key, set 2, vector 9',
|
||||
dict (iv='00'*8)),
|
||||
|
||||
( '00' * 1024,
|
||||
'71daee5142d0728b41b6597933ebf467e43279e30978677078941602629cbf68'
|
||||
+ 'b73d6bd2c95f118d2b3e6ec955dabb6dc61c4143bc9a9b32b99dbe6866166dc0'
|
||||
+ '8631b7d6553050303d7252c264d3a90d26c853634813e09ad7545a6ce7e84a5d'
|
||||
+ 'fc75ec43431207d5319970b0faadb0e1510625bb54372c8515e28e2accf0a993'
|
||||
+ '0ad15f431874923d2a59e20d9f2a5367dba6051564f150287debb1db536ff9b0'
|
||||
+ '9ad981f25e5010d85d76ee0c305f755b25e6f09341e0812f95c94f42eead346e'
|
||||
+ '81f39c58c5faa2c88953dc0cac90469db2063cb5cdb22c9eae22afbf0506fca4'
|
||||
+ '1dc710b846fbdfe3c46883dd118f3a5e8b11b6afd9e71680d8666557301a2daa'
|
||||
+ 'fb9496c559784d35a035360885f9b17bd7191977deea932b981ebdb29057ae3c'
|
||||
+ '92cfeff5e6c5d0cb62f209ce342d4e35c69646ccd14e53350e488bb310a32f8b'
|
||||
+ '0248e70acc5b473df537ced3f81a014d4083932bedd62ed0e447b6766cd2604b'
|
||||
+ '706e9b346c4468beb46a34ecf1610ebd38331d52bf33346afec15eefb2a7699e'
|
||||
+ '8759db5a1f636a48a039688e39de34d995df9f27ed9edc8dd795e39e53d9d925'
|
||||
+ 'b278010565ff665269042f05096d94da3433d957ec13d2fd82a0066283d0d1ee'
|
||||
+ 'b81bf0ef133b7fd90248b8ffb499b2414cd4fa003093ff0864575a43749bf596'
|
||||
+ '02f26c717fa96b1d057697db08ebc3fa664a016a67dcef8807577cc3a09385d3'
|
||||
+ 'f4dc79b34364bb3b166ce65fe1dd28e3950fe6fa81063f7b16ce1c0e6daac1f8'
|
||||
+ '188455b77752045e863c9b256ad92bc6e2d08314c5bba191c274f42dfbb3d652'
|
||||
+ 'bb771956555e880f84cd8b827a4c5a52f3a099fa0259bd4aac3efd541f191170'
|
||||
+ '4412d6e85fbcc628b335875b9fef24807f6e1bc66c3186159e1e7f5a13913e02'
|
||||
+ 'd241ce2efdbcaa275039fb14eac5923d17ffbc7f1abd3b45e92127575bfbabf9'
|
||||
+ '3a257ebef0aa1437b326e41b585af572f7239c33b32981a1577a4f629b027e1e'
|
||||
+ 'b49d58cc497e944d79cef44357c2bf25442ab779651e991147bf79d6fd3a8868'
|
||||
+ '0cd3b1748e07fd10d78aceef6db8a5e563570d40127f754146c34a440f2a991a'
|
||||
+ '23fa39d365141f255041f2135c5cba4373452c114da1801bacca38610e3a6524'
|
||||
+ '2b822d32de4ab5a7d3cf9b61b37493c863bd12e2cae10530cddcda2cb7a5436b'
|
||||
+ 'ef8988d4d24e8cdc31b2d2a3586340bc5141f8f6632d0dd543bfed81eb471ba1'
|
||||
+ 'f3dc2225a15ffddcc03eb48f44e27e2aa390598adf83f15c6608a5f18d4dfcf0'
|
||||
+ 'f547d467a4d70b281c83a595d7660d0b62de78b9cca023cca89d7b1f83484638'
|
||||
+ '0e228c25f049184a612ef5bb3d37454e6cfa5b10dceda619d898a699b3c8981a'
|
||||
+ '173407844bb89b4287bf57dd6600c79e352c681d74b03fa7ea0d7bf6ad69f8a6'
|
||||
+ '8ecb001963bd2dd8a2baa0083ec09751cd9742402ad716be16d5c052304cfca1',
|
||||
'0F62B5085BAE0154A7FA4DA0F34699EC',
|
||||
'128 bits key, Set 6, vector# 3',
|
||||
dict (iv='288FF65DC42B92F9')),
|
||||
|
||||
( '00' * 1024,
|
||||
'5e5e71f90199340304abb22a37b6625bf883fb89ce3b21f54a10b81066ef87da'
|
||||
+ '30b77699aa7379da595c77dd59542da208e5954f89e40eb7aa80a84a6176663f'
|
||||
+ 'd910cde567cf1ff60f7040548d8f376bfd1f44c4774aac37410ede7d5c3463fc'
|
||||
+ '4508a603201d8495ad257894e5eb1914b53e8da5e4bf2bc83ac87ce55cc67df7'
|
||||
+ '093d9853d2a83a9c8be969175df7c807a17156df768445dd0874a9271c6537f5'
|
||||
+ 'ce0466473582375f067fa4fcdaf65dbc0139cd75e8c21a482f28c0fb8c3d9f94'
|
||||
+ '22606cc8e88fe28fe73ec3cb10ff0e8cc5f2a49e540f007265c65b7130bfdb98'
|
||||
+ '795b1df9522da46e48b30e55d9f0d787955ece720205b29c85f3ad9be33b4459'
|
||||
+ '7d21b54d06c9a60b04b8e640c64e566e51566730e86cf128ab14174f91bd8981'
|
||||
+ 'a6fb00fe587bbd6c38b5a1dfdb04ea7e61536fd229f957aa9b070ca931358e85'
|
||||
+ '11b92c53c523cb54828fb1513c5636fa9a0645b4a3c922c0db94986d92f314ff'
|
||||
+ '7852c03b231e4dceea5dd8cced621869cff818daf3c270ff3c8be2e5c74be767'
|
||||
+ 'a4e1fdf3327a934fe31e46df5a74ae2021cee021d958c4f615263d99a5ddae7f'
|
||||
+ 'eab45e6eccbafefe4761c57750847b7e75ee2e2f14333c0779ce4678f47b1e1b'
|
||||
+ '760a03a5f17d6e91d4b42313b3f1077ee270e432fe04917ed1fc8babebf7c941'
|
||||
+ '42b80dfb44a28a2a3e59093027606f6860bfb8c2e5897078cfccda7314c70035'
|
||||
+ 'f137de6f05daa035891d5f6f76e1df0fce1112a2ff0ac2bd3534b5d1bf4c7165'
|
||||
+ 'fb40a1b6eacb7f295711c4907ae457514a7010f3a342b4427593d61ba993bc59'
|
||||
+ '8bd09c56b9ee53aac5dd861fa4b4bb53888952a4aa9d8ca8671582de716270e1'
|
||||
+ '97375b3ee49e51fa2bf4ef32015dd9a764d966aa2ae541592d0aa650849e99ca'
|
||||
+ '5c6c39beebf516457cc32fe4c105bff314a12f1ec94bdf4d626f5d9b1cbbde42'
|
||||
+ 'e5733f0885765ba29e2e82c829d312f5fc7e180679ac84826c08d0a644b326d0'
|
||||
+ '44da0fdcc75fa53cfe4ced0437fa4df5a7ecbca8b4cb7c4a9ecf9a60d00a56eb'
|
||||
+ '81da52adc21f508dbb60a9503a3cc94a896616d86020d5b0e5c637329b6d396a'
|
||||
+ '41a21ba2c4a9493cf33fa2d4f10f77d5b12fdad7e478ccfe79b74851fc96a7ca'
|
||||
+ '6320c5efd561a222c0ab0fb44bbda0e42149611d2262bb7d1719150fa798718a'
|
||||
+ '0eec63ee297cad459869c8b0f06c4e2b56cbac03cd2605b2a924efedf85ec8f1'
|
||||
+ '9b0b6c90e7cbd933223ffeb1b3a3f9677657905829294c4c70acdb8b0891b47d'
|
||||
+ '0875d0cd6c0f4efe2917fc44b581ef0d1e4280197065d07da34ab33283364552'
|
||||
+ 'efad0bd9257b059acdd0a6f246812feb69e7e76065f27dbc2eee94da9cc41835'
|
||||
+ 'bf826e36e5cebe5d4d6a37a6a666246290ce51a0c082718ab0ec855668db1add'
|
||||
+ 'a658e5f257e0db39384d02e6145c4c00eaa079098f6d820d872de711b6ed08cf',
|
||||
'0F62B5085BAE0154A7FA4DA0F34699EC3F92E5388BDE3184D72A7DD02376C91C',
|
||||
'256 bits key, Set 6, vector# 3',
|
||||
dict (iv='288FF65DC42B92F9')),
|
||||
|
||||
]
|
||||
|
||||
|
||||
class KeyLength(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
|
||||
nonce = bchr(0) * 8
|
||||
for key_length in (15, 30, 33):
|
||||
key = bchr(1) * key_length
|
||||
self.assertRaises(ValueError, Salsa20.new, key, nonce)
|
||||
|
||||
|
||||
class NonceTests(unittest.TestCase):
|
||||
|
||||
def test_invalid_nonce_length(self):
|
||||
key = bchr(1) * 16
|
||||
self.assertRaises(ValueError, Salsa20.new, key, bchr(0) * 7)
|
||||
self.assertRaises(ValueError, Salsa20.new, key, bchr(0) * 9)
|
||||
|
||||
def test_default_nonce(self):
|
||||
|
||||
cipher1 = Salsa20.new(bchr(1) * 16)
|
||||
cipher2 = Salsa20.new(bchr(1) * 16)
|
||||
self.assertEqual(len(cipher1.nonce), 8)
|
||||
self.assertNotEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
|
||||
class ByteArrayTest(unittest.TestCase):
|
||||
"""Verify we can encrypt or decrypt bytearrays"""
|
||||
|
||||
def runTest(self):
|
||||
|
||||
data = b"0123"
|
||||
key = b"9" * 32
|
||||
nonce = b"t" * 8
|
||||
|
||||
# Encryption
|
||||
data_ba = bytearray(data)
|
||||
key_ba = bytearray(key)
|
||||
nonce_ba = bytearray(nonce)
|
||||
|
||||
cipher1 = Salsa20.new(key=key, nonce=nonce)
|
||||
ct = cipher1.encrypt(data)
|
||||
|
||||
cipher2 = Salsa20.new(key=key_ba, nonce=nonce_ba)
|
||||
key_ba[:1] = b'\xFF'
|
||||
nonce_ba[:1] = b'\xFF'
|
||||
ct_test = cipher2.encrypt(data_ba)
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decryption
|
||||
key_ba = bytearray(key)
|
||||
nonce_ba = bytearray(nonce)
|
||||
ct_ba = bytearray(ct)
|
||||
|
||||
cipher3 = Salsa20.new(key=key_ba, nonce=nonce_ba)
|
||||
key_ba[:1] = b'\xFF'
|
||||
nonce_ba[:1] = b'\xFF'
|
||||
pt_test = cipher3.decrypt(ct_ba)
|
||||
|
||||
self.assertEqual(data, pt_test)
|
||||
|
||||
|
||||
class MemoryviewTest(unittest.TestCase):
|
||||
"""Verify we can encrypt or decrypt bytearrays"""
|
||||
|
||||
def runTest(self):
|
||||
|
||||
data = b"0123"
|
||||
key = b"9" * 32
|
||||
nonce = b"t" * 8
|
||||
|
||||
# Encryption
|
||||
data_mv = memoryview(bytearray(data))
|
||||
key_mv = memoryview(bytearray(key))
|
||||
nonce_mv = memoryview(bytearray(nonce))
|
||||
|
||||
cipher1 = Salsa20.new(key=key, nonce=nonce)
|
||||
ct = cipher1.encrypt(data)
|
||||
|
||||
cipher2 = Salsa20.new(key=key_mv, nonce=nonce_mv)
|
||||
key_mv[:1] = b'\xFF'
|
||||
nonce_mv[:1] = b'\xFF'
|
||||
ct_test = cipher2.encrypt(data_mv)
|
||||
|
||||
self.assertEqual(ct, ct_test)
|
||||
self.assertEqual(cipher1.nonce, cipher2.nonce)
|
||||
|
||||
# Decryption
|
||||
key_mv = memoryview(bytearray(key))
|
||||
nonce_mv = memoryview(bytearray(nonce))
|
||||
ct_mv = memoryview(bytearray(ct))
|
||||
|
||||
cipher3 = Salsa20.new(key=key_mv, nonce=nonce_mv)
|
||||
key_mv[:1] = b'\xFF'
|
||||
nonce_mv[:1] = b'\xFF'
|
||||
pt_test = cipher3.decrypt(ct_mv)
|
||||
|
||||
self.assertEqual(data, pt_test)
|
||||
|
||||
|
||||
class TestOutput(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
# Encrypt/Decrypt data and test output parameter
|
||||
|
||||
key = b'4' * 32
|
||||
nonce = b'5' * 8
|
||||
cipher = Salsa20.new(key=key, nonce=nonce)
|
||||
|
||||
pt = b'5' * 300
|
||||
ct = cipher.encrypt(pt)
|
||||
|
||||
output = bytearray(len(pt))
|
||||
cipher = Salsa20.new(key=key, nonce=nonce)
|
||||
res = cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
cipher = Salsa20.new(key=key, nonce=nonce)
|
||||
res = cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
self.assertEqual(res, None)
|
||||
|
||||
output = memoryview(bytearray(len(pt)))
|
||||
cipher = Salsa20.new(key=key, nonce=nonce)
|
||||
cipher.encrypt(pt, output=output)
|
||||
self.assertEqual(ct, output)
|
||||
|
||||
cipher = Salsa20.new(key=key, nonce=nonce)
|
||||
cipher.decrypt(ct, output=output)
|
||||
self.assertEqual(pt, output)
|
||||
|
||||
cipher = Salsa20.new(key=key, nonce=nonce)
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*len(pt))
|
||||
|
||||
cipher = Salsa20.new(key=key, nonce=nonce)
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*len(ct))
|
||||
|
||||
shorter_output = bytearray(len(pt) - 1)
|
||||
|
||||
cipher = Salsa20.new(key=key, nonce=nonce)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output)
|
||||
|
||||
cipher = Salsa20.new(key=key, nonce=nonce)
|
||||
self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = make_stream_tests(Salsa20, "Salsa20", test_data)
|
||||
tests.append(KeyLength())
|
||||
tests += list_test_cases(NonceTests)
|
||||
tests.append(ByteArrayTest())
|
||||
tests.append(MemoryviewTest())
|
||||
tests.append(TestOutput())
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
@@ -0,0 +1,283 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 encryption
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.SelfTest.st_common import list_test_cases, a2b_hex
|
||||
from Crypto import Random
|
||||
from Crypto.Cipher import PKCS1_v1_5 as PKCS
|
||||
from Crypto.Util.py3compat import b
|
||||
from Crypto.Util.number import bytes_to_long, long_to_bytes
|
||||
from Crypto.SelfTest.loader import load_test_vectors_wycheproof
|
||||
|
||||
|
||||
def rws(t):
|
||||
"""Remove white spaces, tabs, and new lines from a string"""
|
||||
for c in ['\n', '\t', ' ']:
|
||||
t = t.replace(c, '')
|
||||
return t
|
||||
|
||||
|
||||
def t2b(t):
|
||||
"""Convert a text string with bytes in hex form to a byte string"""
|
||||
clean = b(rws(t))
|
||||
if len(clean) % 2 == 1:
|
||||
raise ValueError("Even number of characters expected")
|
||||
return a2b_hex(clean)
|
||||
|
||||
|
||||
class PKCS1_15_Tests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.rng = Random.new().read
|
||||
self.key1024 = RSA.generate(1024, self.rng)
|
||||
|
||||
# List of tuples with test data for PKCS#1 v1.5.
|
||||
# Each tuple is made up by:
|
||||
# Item #0: dictionary with RSA key component, or key to import
|
||||
# Item #1: plaintext
|
||||
# Item #2: ciphertext
|
||||
# Item #3: random data
|
||||
|
||||
_testData = (
|
||||
|
||||
#
|
||||
# Generated with openssl 0.9.8o
|
||||
#
|
||||
(
|
||||
# Private key
|
||||
'''-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQDAiAnvIAOvqVwJTaYzsKnefZftgtXGE2hPJppGsWl78yz9jeXY
|
||||
W/FxX/gTPURArNhdnhP6n3p2ZaDIBrO2zizbgIXs0IsljTTcr4vnI8fMXzyNUOjA
|
||||
zP3nzMqZDZK6757XQAobOssMkBFqRWwilT/3DsBhRpl3iMUhF+wvpTSHewIDAQAB
|
||||
AoGAC4HV/inOrpgTvSab8Wj0riyZgQOZ3U3ZpSlsfR8ra9Ib9Uee3jCYnKscu6Gk
|
||||
y6zI/cdt8EPJ4PuwAWSNJzbpbVaDvUq25OD+CX8/uRT08yBS4J8TzBitZJTD4lS7
|
||||
atdTnKT0Wmwk+u8tDbhvMKwnUHdJLcuIsycts9rwJVapUtkCQQDvDpx2JMun0YKG
|
||||
uUttjmL8oJ3U0m3ZvMdVwBecA0eebZb1l2J5PvI3EJD97eKe91Nsw8T3lwpoN40k
|
||||
IocSVDklAkEAzi1HLHE6EzVPOe5+Y0kGvrIYRRhncOb72vCvBZvD6wLZpQgqo6c4
|
||||
d3XHFBBQWA6xcvQb5w+VVEJZzw64y25sHwJBAMYReRl6SzL0qA0wIYrYWrOt8JeQ
|
||||
8mthulcWHXmqTgC6FEXP9Es5GD7/fuKl4wqLKZgIbH4nqvvGay7xXLCXD/ECQH9a
|
||||
1JYNMtRen5unSAbIOxRcKkWz92F0LKpm9ZW/S9vFHO+mBcClMGoKJHiuQxLBsLbT
|
||||
NtEZfSJZAeS2sUtn3/0CQDb2M2zNBTF8LlM0nxmh0k9VGm5TVIyBEMcipmvOgqIs
|
||||
HKukWBcq9f/UOmS0oEhai/6g+Uf7VHJdWaeO5LzuvwU=
|
||||
-----END RSA PRIVATE KEY-----''',
|
||||
# Plaintext
|
||||
'''THIS IS PLAINTEXT\x0A''',
|
||||
# Ciphertext
|
||||
'''3f dc fd 3c cd 5c 9b 12 af 65 32 e3 f7 d0 da 36
|
||||
8f 8f d9 e3 13 1c 7f c8 b3 f9 c1 08 e4 eb 79 9c
|
||||
91 89 1f 96 3b 94 77 61 99 a4 b1 ee 5d e6 17 c9
|
||||
5d 0a b5 63 52 0a eb 00 45 38 2a fb b0 71 3d 11
|
||||
f7 a1 9e a7 69 b3 af 61 c0 bb 04 5b 5d 4b 27 44
|
||||
1f 5b 97 89 ba 6a 08 95 ee 4f a2 eb 56 64 e5 0f
|
||||
da 7c f9 9a 61 61 06 62 ed a0 bc 5f aa 6c 31 78
|
||||
70 28 1a bb 98 3c e3 6a 60 3c d1 0b 0f 5a f4 75''',
|
||||
# Random data
|
||||
'''eb d7 7d 86 a4 35 23 a3 54 7e 02 0b 42 1d
|
||||
61 6c af 67 b8 4e 17 56 80 66 36 04 64 34 26 8a
|
||||
47 dd 44 b3 1a b2 17 60 f4 91 2e e2 b5 95 64 cc
|
||||
f9 da c8 70 94 54 86 4c ef 5b 08 7d 18 c4 ab 8d
|
||||
04 06 33 8f ca 15 5f 52 60 8a a1 0c f5 08 b5 4c
|
||||
bb 99 b8 94 25 04 9c e6 01 75 e6 f9 63 7a 65 61
|
||||
13 8a a7 47 77 81 ae 0d b8 2c 4d 50 a5'''
|
||||
),
|
||||
)
|
||||
|
||||
def testEncrypt1(self):
|
||||
for test in self._testData:
|
||||
# Build the key
|
||||
key = RSA.importKey(test[0])
|
||||
# RNG that takes its random numbers from a pool given
|
||||
# at initialization
|
||||
class randGen:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.idx = 0
|
||||
def __call__(self, N):
|
||||
r = self.data[self.idx:self.idx+N]
|
||||
self.idx += N
|
||||
return r
|
||||
# The real test
|
||||
cipher = PKCS.new(key, randfunc=randGen(t2b(test[3])))
|
||||
ct = cipher.encrypt(b(test[1]))
|
||||
self.assertEqual(ct, t2b(test[2]))
|
||||
|
||||
def testEncrypt2(self):
|
||||
# Verify that encryption fail if plaintext is too long
|
||||
pt = '\x00'*(128-11+1)
|
||||
cipher = PKCS.new(self.key1024)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt)
|
||||
|
||||
def testVerify1(self):
|
||||
for test in self._testData:
|
||||
key = RSA.importKey(test[0])
|
||||
expected_pt = b(test[1])
|
||||
ct = t2b(test[2])
|
||||
cipher = PKCS.new(key)
|
||||
|
||||
# The real test
|
||||
pt = cipher.decrypt(ct, None)
|
||||
self.assertEqual(pt, expected_pt)
|
||||
|
||||
pt = cipher.decrypt(ct, b'\xFF' * len(expected_pt))
|
||||
self.assertEqual(pt, expected_pt)
|
||||
|
||||
def testVerify2(self):
|
||||
# Verify that decryption fails if ciphertext is not as long as
|
||||
# RSA modulus
|
||||
cipher = PKCS.new(self.key1024)
|
||||
self.assertRaises(ValueError, cipher.decrypt, '\x00'*127, "---")
|
||||
self.assertRaises(ValueError, cipher.decrypt, '\x00'*129, "---")
|
||||
|
||||
# Verify that decryption fails if there are less then 8 non-zero padding
|
||||
# bytes
|
||||
pt = b('\x00\x02' + '\xFF'*7 + '\x00' + '\x45'*118)
|
||||
pt_int = bytes_to_long(pt)
|
||||
ct_int = self.key1024._encrypt(pt_int)
|
||||
ct = long_to_bytes(ct_int, 128)
|
||||
self.assertEqual(b"---", cipher.decrypt(ct, b"---"))
|
||||
|
||||
def testEncryptVerify1(self):
|
||||
# Encrypt/Verify messages of length [0..RSAlen-11]
|
||||
# and therefore padding [8..117]
|
||||
for pt_len in range(0, 128 - 11 + 1):
|
||||
pt = self.rng(pt_len)
|
||||
cipher = PKCS.new(self.key1024)
|
||||
ct = cipher.encrypt(pt)
|
||||
pt2 = cipher.decrypt(ct, b'\xAA' * pt_len)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_encrypt_verify_exp_pt_len(self):
|
||||
|
||||
cipher = PKCS.new(self.key1024)
|
||||
pt = b'5' * 16
|
||||
ct = cipher.encrypt(pt)
|
||||
sentinel = b'\xAA' * 16
|
||||
|
||||
pt_A = cipher.decrypt(ct, sentinel, 16)
|
||||
self.assertEqual(pt, pt_A)
|
||||
|
||||
pt_B = cipher.decrypt(ct, sentinel, 15)
|
||||
self.assertEqual(sentinel, pt_B)
|
||||
|
||||
pt_C = cipher.decrypt(ct, sentinel, 17)
|
||||
self.assertEqual(sentinel, pt_C)
|
||||
|
||||
def testByteArray(self):
|
||||
pt = b"XER"
|
||||
cipher = PKCS.new(self.key1024)
|
||||
ct = cipher.encrypt(bytearray(pt))
|
||||
pt2 = cipher.decrypt(bytearray(ct), '\xFF' * len(pt))
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def testMemoryview(self):
|
||||
pt = b"XER"
|
||||
cipher = PKCS.new(self.key1024)
|
||||
ct = cipher.encrypt(memoryview(bytearray(pt)))
|
||||
pt2 = cipher.decrypt(memoryview(bytearray(ct)), b'\xFF' * len(pt))
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def test_return_type(self):
|
||||
pt = b"XYZ"
|
||||
cipher = PKCS.new(self.key1024)
|
||||
ct = cipher.encrypt(pt)
|
||||
self.assertTrue(isinstance(ct, bytes))
|
||||
pt2 = cipher.decrypt(ct, b'\xAA' * 3)
|
||||
self.assertTrue(isinstance(pt2, bytes))
|
||||
|
||||
|
||||
class TestVectorsWycheproof(unittest.TestCase):
|
||||
|
||||
def __init__(self, wycheproof_warnings, skip_slow_tests):
|
||||
unittest.TestCase.__init__(self)
|
||||
self._wycheproof_warnings = wycheproof_warnings
|
||||
self._skip_slow_tests = skip_slow_tests
|
||||
self._id = "None"
|
||||
|
||||
def load_tests(self, filename):
|
||||
|
||||
def filter_rsa(group):
|
||||
return RSA.import_key(group['privateKeyPem'])
|
||||
|
||||
result = load_test_vectors_wycheproof(("Cipher", "wycheproof"),
|
||||
filename,
|
||||
"Wycheproof PKCS#1v1.5 (%s)" % filename,
|
||||
group_tag={'rsa_key': filter_rsa}
|
||||
)
|
||||
return result
|
||||
|
||||
def setUp(self):
|
||||
self.tv = []
|
||||
self.tv.extend(self.load_tests("rsa_pkcs1_2048_test.json"))
|
||||
if not self._skip_slow_tests:
|
||||
self.tv.extend(self.load_tests("rsa_pkcs1_3072_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_pkcs1_4096_test.json"))
|
||||
|
||||
def shortDescription(self):
|
||||
return self._id
|
||||
|
||||
def warn(self, tv):
|
||||
if tv.warning and self._wycheproof_warnings:
|
||||
import warnings
|
||||
warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment))
|
||||
|
||||
def test_decrypt(self, tv):
|
||||
self._id = "Wycheproof Decrypt PKCS#1v1.5 Test #%s" % tv.id
|
||||
sentinel = b'\xAA' * max(3, len(tv.msg))
|
||||
cipher = PKCS.new(tv.rsa_key)
|
||||
try:
|
||||
pt = cipher.decrypt(tv.ct, sentinel=sentinel)
|
||||
except ValueError:
|
||||
assert not tv.valid
|
||||
else:
|
||||
if pt == sentinel:
|
||||
assert not tv.valid
|
||||
else:
|
||||
assert tv.valid
|
||||
self.assertEqual(pt, tv.msg)
|
||||
self.warn(tv)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
for tv in self.tv:
|
||||
self.test_decrypt(tv)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
skip_slow_tests = not config.get('slow_tests')
|
||||
wycheproof_warnings = config.get('wycheproof_warnings')
|
||||
|
||||
tests = []
|
||||
tests += list_test_cases(PKCS1_15_Tests)
|
||||
tests += [TestVectorsWycheproof(wycheproof_warnings, skip_slow_tests)]
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def suite():
|
||||
return unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
@@ -0,0 +1,506 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/test_pkcs1_oaep.py: Self-test for PKCS#1 OAEP encryption
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases, a2b_hex
|
||||
from Crypto.SelfTest.loader import load_test_vectors_wycheproof
|
||||
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Cipher import PKCS1_OAEP as PKCS
|
||||
from Crypto.Hash import MD2, MD5, SHA1, SHA256, RIPEMD160, SHA224, SHA384, SHA512
|
||||
from Crypto import Random
|
||||
from Crypto.Signature.pss import MGF1
|
||||
|
||||
from Crypto.Util.py3compat import b, bchr
|
||||
|
||||
|
||||
def rws(t):
|
||||
"""Remove white spaces, tabs, and new lines from a string"""
|
||||
for c in ['\n', '\t', ' ']:
|
||||
t = t.replace(c, '')
|
||||
return t
|
||||
|
||||
|
||||
def t2b(t):
|
||||
"""Convert a text string with bytes in hex form to a byte string"""
|
||||
clean = rws(t)
|
||||
if len(clean) % 2 == 1:
|
||||
raise ValueError("Even number of characters expected")
|
||||
return a2b_hex(clean)
|
||||
|
||||
|
||||
class PKCS1_OAEP_Tests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.rng = Random.new().read
|
||||
self.key1024 = RSA.generate(1024, self.rng)
|
||||
|
||||
# List of tuples with test data for PKCS#1 OAEP
|
||||
# Each tuple is made up by:
|
||||
# Item #0: dictionary with RSA key component
|
||||
# Item #1: plaintext
|
||||
# Item #2: ciphertext
|
||||
# Item #3: random data (=seed)
|
||||
# Item #4: hash object
|
||||
|
||||
_testData = (
|
||||
|
||||
#
|
||||
# From in oaep-int.txt to be found in
|
||||
# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
|
||||
#
|
||||
(
|
||||
# Private key
|
||||
{
|
||||
'n':'''bb f8 2f 09 06 82 ce 9c 23 38 ac 2b 9d a8 71 f7
|
||||
36 8d 07 ee d4 10 43 a4 40 d6 b6 f0 74 54 f5 1f
|
||||
b8 df ba af 03 5c 02 ab 61 ea 48 ce eb 6f cd 48
|
||||
76 ed 52 0d 60 e1 ec 46 19 71 9d 8a 5b 8b 80 7f
|
||||
af b8 e0 a3 df c7 37 72 3e e6 b4 b7 d9 3a 25 84
|
||||
ee 6a 64 9d 06 09 53 74 88 34 b2 45 45 98 39 4e
|
||||
e0 aa b1 2d 7b 61 a5 1f 52 7a 9a 41 f6 c1 68 7f
|
||||
e2 53 72 98 ca 2a 8f 59 46 f8 e5 fd 09 1d bd cb''',
|
||||
# Public key
|
||||
'e':'11',
|
||||
# In the test vector, only p and q were given...
|
||||
# d is computed offline as e^{-1} mod (p-1)(q-1)
|
||||
'd':'''a5dafc5341faf289c4b988db30c1cdf83f31251e0
|
||||
668b42784813801579641b29410b3c7998d6bc465745e5c3
|
||||
92669d6870da2c082a939e37fdcb82ec93edac97ff3ad595
|
||||
0accfbc111c76f1a9529444e56aaf68c56c092cd38dc3bef
|
||||
5d20a939926ed4f74a13eddfbe1a1cecc4894af9428c2b7b
|
||||
8883fe4463a4bc85b1cb3c1'''
|
||||
}
|
||||
,
|
||||
# Plaintext
|
||||
'''d4 36 e9 95 69 fd 32 a7 c8 a0 5b bc 90 d3 2c 49''',
|
||||
# Ciphertext
|
||||
'''12 53 e0 4d c0 a5 39 7b b4 4a 7a b8 7e 9b f2 a0
|
||||
39 a3 3d 1e 99 6f c8 2a 94 cc d3 00 74 c9 5d f7
|
||||
63 72 20 17 06 9e 52 68 da 5d 1c 0b 4f 87 2c f6
|
||||
53 c1 1d f8 23 14 a6 79 68 df ea e2 8d ef 04 bb
|
||||
6d 84 b1 c3 1d 65 4a 19 70 e5 78 3b d6 eb 96 a0
|
||||
24 c2 ca 2f 4a 90 fe 9f 2e f5 c9 c1 40 e5 bb 48
|
||||
da 95 36 ad 87 00 c8 4f c9 13 0a de a7 4e 55 8d
|
||||
51 a7 4d df 85 d8 b5 0d e9 68 38 d6 06 3e 09 55''',
|
||||
# Random
|
||||
'''aa fd 12 f6 59 ca e6 34 89 b4 79 e5 07 6d de c2
|
||||
f0 6c b5 8f''',
|
||||
# Hash
|
||||
SHA1,
|
||||
),
|
||||
|
||||
#
|
||||
# From in oaep-vect.txt to be found in Example 1.1
|
||||
# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
|
||||
#
|
||||
(
|
||||
# Private key
|
||||
{
|
||||
'n':'''a8 b3 b2 84 af 8e b5 0b 38 70 34 a8 60 f1 46 c4
|
||||
91 9f 31 87 63 cd 6c 55 98 c8 ae 48 11 a1 e0 ab
|
||||
c4 c7 e0 b0 82 d6 93 a5 e7 fc ed 67 5c f4 66 85
|
||||
12 77 2c 0c bc 64 a7 42 c6 c6 30 f5 33 c8 cc 72
|
||||
f6 2a e8 33 c4 0b f2 58 42 e9 84 bb 78 bd bf 97
|
||||
c0 10 7d 55 bd b6 62 f5 c4 e0 fa b9 84 5c b5 14
|
||||
8e f7 39 2d d3 aa ff 93 ae 1e 6b 66 7b b3 d4 24
|
||||
76 16 d4 f5 ba 10 d4 cf d2 26 de 88 d3 9f 16 fb''',
|
||||
'e':'''01 00 01''',
|
||||
'd':'''53 33 9c fd b7 9f c8 46 6a 65 5c 73 16 ac a8 5c
|
||||
55 fd 8f 6d d8 98 fd af 11 95 17 ef 4f 52 e8 fd
|
||||
8e 25 8d f9 3f ee 18 0f a0 e4 ab 29 69 3c d8 3b
|
||||
15 2a 55 3d 4a c4 d1 81 2b 8b 9f a5 af 0e 7f 55
|
||||
fe 73 04 df 41 57 09 26 f3 31 1f 15 c4 d6 5a 73
|
||||
2c 48 31 16 ee 3d 3d 2d 0a f3 54 9a d9 bf 7c bf
|
||||
b7 8a d8 84 f8 4d 5b eb 04 72 4d c7 36 9b 31 de
|
||||
f3 7d 0c f5 39 e9 cf cd d3 de 65 37 29 ea d5 d1 '''
|
||||
}
|
||||
,
|
||||
# Plaintext
|
||||
'''66 28 19 4e 12 07 3d b0 3b a9 4c da 9e f9 53 23
|
||||
97 d5 0d ba 79 b9 87 00 4a fe fe 34''',
|
||||
# Ciphertext
|
||||
'''35 4f e6 7b 4a 12 6d 5d 35 fe 36 c7 77 79 1a 3f
|
||||
7b a1 3d ef 48 4e 2d 39 08 af f7 22 fa d4 68 fb
|
||||
21 69 6d e9 5d 0b e9 11 c2 d3 17 4f 8a fc c2 01
|
||||
03 5f 7b 6d 8e 69 40 2d e5 45 16 18 c2 1a 53 5f
|
||||
a9 d7 bf c5 b8 dd 9f c2 43 f8 cf 92 7d b3 13 22
|
||||
d6 e8 81 ea a9 1a 99 61 70 e6 57 a0 5a 26 64 26
|
||||
d9 8c 88 00 3f 84 77 c1 22 70 94 a0 d9 fa 1e 8c
|
||||
40 24 30 9c e1 ec cc b5 21 00 35 d4 7a c7 2e 8a''',
|
||||
# Random
|
||||
'''18 b7 76 ea 21 06 9d 69 77 6a 33 e9 6b ad 48 e1
|
||||
dd a0 a5 ef''',
|
||||
SHA1
|
||||
),
|
||||
|
||||
#
|
||||
# From in oaep-vect.txt to be found in Example 2.1
|
||||
# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
|
||||
#
|
||||
(
|
||||
# Private key
|
||||
{
|
||||
'n':'''01 94 7c 7f ce 90 42 5f 47 27 9e 70 85 1f 25 d5
|
||||
e6 23 16 fe 8a 1d f1 93 71 e3 e6 28 e2 60 54 3e
|
||||
49 01 ef 60 81 f6 8c 0b 81 41 19 0d 2a e8 da ba
|
||||
7d 12 50 ec 6d b6 36 e9 44 ec 37 22 87 7c 7c 1d
|
||||
0a 67 f1 4b 16 94 c5 f0 37 94 51 a4 3e 49 a3 2d
|
||||
de 83 67 0b 73 da 91 a1 c9 9b c2 3b 43 6a 60 05
|
||||
5c 61 0f 0b af 99 c1 a0 79 56 5b 95 a3 f1 52 66
|
||||
32 d1 d4 da 60 f2 0e da 25 e6 53 c4 f0 02 76 6f
|
||||
45''',
|
||||
'e':'''01 00 01''',
|
||||
'd':'''08 23 f2 0f ad b5 da 89 08 8a 9d 00 89 3e 21 fa
|
||||
4a 1b 11 fb c9 3c 64 a3 be 0b aa ea 97 fb 3b 93
|
||||
c3 ff 71 37 04 c1 9c 96 3c 1d 10 7a ae 99 05 47
|
||||
39 f7 9e 02 e1 86 de 86 f8 7a 6d de fe a6 d8 cc
|
||||
d1 d3 c8 1a 47 bf a7 25 5b e2 06 01 a4 a4 b2 f0
|
||||
8a 16 7b 5e 27 9d 71 5b 1b 45 5b dd 7e ab 24 59
|
||||
41 d9 76 8b 9a ce fb 3c cd a5 95 2d a3 ce e7 25
|
||||
25 b4 50 16 63 a8 ee 15 c9 e9 92 d9 24 62 fe 39'''
|
||||
},
|
||||
# Plaintext
|
||||
'''8f f0 0c aa 60 5c 70 28 30 63 4d 9a 6c 3d 42 c6
|
||||
52 b5 8c f1 d9 2f ec 57 0b ee e7''',
|
||||
# Ciphertext
|
||||
'''01 81 af 89 22 b9 fc b4 d7 9d 92 eb e1 98 15 99
|
||||
2f c0 c1 43 9d 8b cd 49 13 98 a0 f4 ad 3a 32 9a
|
||||
5b d9 38 55 60 db 53 26 83 c8 b7 da 04 e4 b1 2a
|
||||
ed 6a ac df 47 1c 34 c9 cd a8 91 ad dc c2 df 34
|
||||
56 65 3a a6 38 2e 9a e5 9b 54 45 52 57 eb 09 9d
|
||||
56 2b be 10 45 3f 2b 6d 13 c5 9c 02 e1 0f 1f 8a
|
||||
bb 5d a0 d0 57 09 32 da cf 2d 09 01 db 72 9d 0f
|
||||
ef cc 05 4e 70 96 8e a5 40 c8 1b 04 bc ae fe 72
|
||||
0e''',
|
||||
# Random
|
||||
'''8c 40 7b 5e c2 89 9e 50 99 c5 3e 8c e7 93 bf 94
|
||||
e7 1b 17 82''',
|
||||
SHA1
|
||||
),
|
||||
|
||||
#
|
||||
# From in oaep-vect.txt to be found in Example 10.1
|
||||
# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
|
||||
#
|
||||
(
|
||||
# Private key
|
||||
{
|
||||
'n':'''ae 45 ed 56 01 ce c6 b8 cc 05 f8 03 93 5c 67 4d
|
||||
db e0 d7 5c 4c 09 fd 79 51 fc 6b 0c ae c3 13 a8
|
||||
df 39 97 0c 51 8b ff ba 5e d6 8f 3f 0d 7f 22 a4
|
||||
02 9d 41 3f 1a e0 7e 4e be 9e 41 77 ce 23 e7 f5
|
||||
40 4b 56 9e 4e e1 bd cf 3c 1f b0 3e f1 13 80 2d
|
||||
4f 85 5e b9 b5 13 4b 5a 7c 80 85 ad ca e6 fa 2f
|
||||
a1 41 7e c3 76 3b e1 71 b0 c6 2b 76 0e de 23 c1
|
||||
2a d9 2b 98 08 84 c6 41 f5 a8 fa c2 6b da d4 a0
|
||||
33 81 a2 2f e1 b7 54 88 50 94 c8 25 06 d4 01 9a
|
||||
53 5a 28 6a fe b2 71 bb 9b a5 92 de 18 dc f6 00
|
||||
c2 ae ea e5 6e 02 f7 cf 79 fc 14 cf 3b dc 7c d8
|
||||
4f eb bb f9 50 ca 90 30 4b 22 19 a7 aa 06 3a ef
|
||||
a2 c3 c1 98 0e 56 0c d6 4a fe 77 95 85 b6 10 76
|
||||
57 b9 57 85 7e fd e6 01 09 88 ab 7d e4 17 fc 88
|
||||
d8 f3 84 c4 e6 e7 2c 3f 94 3e 0c 31 c0 c4 a5 cc
|
||||
36 f8 79 d8 a3 ac 9d 7d 59 86 0e aa da 6b 83 bb''',
|
||||
'e':'''01 00 01''',
|
||||
'd':'''05 6b 04 21 6f e5 f3 54 ac 77 25 0a 4b 6b 0c 85
|
||||
25 a8 5c 59 b0 bd 80 c5 64 50 a2 2d 5f 43 8e 59
|
||||
6a 33 3a a8 75 e2 91 dd 43 f4 8c b8 8b 9d 5f c0
|
||||
d4 99 f9 fc d1 c3 97 f9 af c0 70 cd 9e 39 8c 8d
|
||||
19 e6 1d b7 c7 41 0a 6b 26 75 df bf 5d 34 5b 80
|
||||
4d 20 1a dd 50 2d 5c e2 df cb 09 1c e9 99 7b be
|
||||
be 57 30 6f 38 3e 4d 58 81 03 f0 36 f7 e8 5d 19
|
||||
34 d1 52 a3 23 e4 a8 db 45 1d 6f 4a 5b 1b 0f 10
|
||||
2c c1 50 e0 2f ee e2 b8 8d ea 4a d4 c1 ba cc b2
|
||||
4d 84 07 2d 14 e1 d2 4a 67 71 f7 40 8e e3 05 64
|
||||
fb 86 d4 39 3a 34 bc f0 b7 88 50 1d 19 33 03 f1
|
||||
3a 22 84 b0 01 f0 f6 49 ea f7 93 28 d4 ac 5c 43
|
||||
0a b4 41 49 20 a9 46 0e d1 b7 bc 40 ec 65 3e 87
|
||||
6d 09 ab c5 09 ae 45 b5 25 19 01 16 a0 c2 61 01
|
||||
84 82 98 50 9c 1c 3b f3 a4 83 e7 27 40 54 e1 5e
|
||||
97 07 50 36 e9 89 f6 09 32 80 7b 52 57 75 1e 79'''
|
||||
},
|
||||
# Plaintext
|
||||
'''8b ba 6b f8 2a 6c 0f 86 d5 f1 75 6e 97 95 68 70
|
||||
b0 89 53 b0 6b 4e b2 05 bc 16 94 ee''',
|
||||
# Ciphertext
|
||||
'''53 ea 5d c0 8c d2 60 fb 3b 85 85 67 28 7f a9 15
|
||||
52 c3 0b 2f eb fb a2 13 f0 ae 87 70 2d 06 8d 19
|
||||
ba b0 7f e5 74 52 3d fb 42 13 9d 68 c3 c5 af ee
|
||||
e0 bf e4 cb 79 69 cb f3 82 b8 04 d6 e6 13 96 14
|
||||
4e 2d 0e 60 74 1f 89 93 c3 01 4b 58 b9 b1 95 7a
|
||||
8b ab cd 23 af 85 4f 4c 35 6f b1 66 2a a7 2b fc
|
||||
c7 e5 86 55 9d c4 28 0d 16 0c 12 67 85 a7 23 eb
|
||||
ee be ff 71 f1 15 94 44 0a ae f8 7d 10 79 3a 87
|
||||
74 a2 39 d4 a0 4c 87 fe 14 67 b9 da f8 52 08 ec
|
||||
6c 72 55 79 4a 96 cc 29 14 2f 9a 8b d4 18 e3 c1
|
||||
fd 67 34 4b 0c d0 82 9d f3 b2 be c6 02 53 19 62
|
||||
93 c6 b3 4d 3f 75 d3 2f 21 3d d4 5c 62 73 d5 05
|
||||
ad f4 cc ed 10 57 cb 75 8f c2 6a ee fa 44 12 55
|
||||
ed 4e 64 c1 99 ee 07 5e 7f 16 64 61 82 fd b4 64
|
||||
73 9b 68 ab 5d af f0 e6 3e 95 52 01 68 24 f0 54
|
||||
bf 4d 3c 8c 90 a9 7b b6 b6 55 32 84 eb 42 9f cc''',
|
||||
# Random
|
||||
'''47 e1 ab 71 19 fe e5 6c 95 ee 5e aa d8 6f 40 d0
|
||||
aa 63 bd 33''',
|
||||
SHA1
|
||||
),
|
||||
)
|
||||
|
||||
def testEncrypt1(self):
|
||||
# Verify encryption using all test vectors
|
||||
for test in self._testData:
|
||||
# Build the key
|
||||
comps = [int(rws(test[0][x]), 16) for x in ('n', 'e')]
|
||||
key = RSA.construct(comps)
|
||||
|
||||
# RNG that takes its random numbers from a pool given
|
||||
# at initialization
|
||||
class randGen:
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.idx = 0
|
||||
|
||||
def __call__(self, N):
|
||||
r = self.data[self.idx:N]
|
||||
self.idx += N
|
||||
return r
|
||||
|
||||
# The real test
|
||||
cipher = PKCS.new(key, test[4], randfunc=randGen(t2b(test[3])))
|
||||
ct = cipher.encrypt(t2b(test[1]))
|
||||
self.assertEqual(ct, t2b(test[2]))
|
||||
|
||||
def testEncrypt2(self):
|
||||
# Verify that encryption fails if plaintext is too long
|
||||
pt = '\x00'*(128-2*20-2+1)
|
||||
cipher = PKCS.new(self.key1024)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt)
|
||||
|
||||
def testDecrypt1(self):
|
||||
# Verify decryption using all test vectors
|
||||
for test in self._testData:
|
||||
# Build the key
|
||||
comps = [int(rws(test[0][x]),16) for x in ('n', 'e', 'd')]
|
||||
key = RSA.construct(comps)
|
||||
# The real test
|
||||
cipher = PKCS.new(key, test[4])
|
||||
pt = cipher.decrypt(t2b(test[2]))
|
||||
self.assertEqual(pt, t2b(test[1]))
|
||||
|
||||
def testDecrypt2(self):
|
||||
# Simplest possible negative tests
|
||||
for ct_size in (127, 128, 129):
|
||||
cipher = PKCS.new(self.key1024)
|
||||
self.assertRaises(ValueError, cipher.decrypt, bchr(0x00)*ct_size)
|
||||
|
||||
def testEncryptDecrypt1(self):
|
||||
# Encrypt/Decrypt messages of length [0..128-2*20-2]
|
||||
for pt_len in range(0, 128-2*20-2):
|
||||
pt = self.rng(pt_len)
|
||||
cipher = PKCS.new(self.key1024)
|
||||
ct = cipher.encrypt(pt)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def testEncryptDecrypt2(self):
|
||||
# Helper function to monitor what's requested from RNG
|
||||
global asked
|
||||
|
||||
def localRng(N):
|
||||
global asked
|
||||
asked += N
|
||||
return self.rng(N)
|
||||
|
||||
# Verify that OAEP is friendly to all hashes
|
||||
for hashmod in (MD2, MD5, SHA1, SHA256, RIPEMD160):
|
||||
# Verify that encrypt() asks for as many random bytes
|
||||
# as the hash output size
|
||||
asked = 0
|
||||
pt = self.rng(40)
|
||||
cipher = PKCS.new(self.key1024, hashmod, randfunc=localRng)
|
||||
ct = cipher.encrypt(pt)
|
||||
self.assertEqual(cipher.decrypt(ct), pt)
|
||||
self.assertEqual(asked, hashmod.digest_size)
|
||||
|
||||
def testEncryptDecrypt3(self):
|
||||
# Verify that OAEP supports labels
|
||||
pt = self.rng(35)
|
||||
xlabel = self.rng(22)
|
||||
cipher = PKCS.new(self.key1024, label=xlabel)
|
||||
ct = cipher.encrypt(pt)
|
||||
self.assertEqual(cipher.decrypt(ct), pt)
|
||||
|
||||
def testEncryptDecrypt4(self):
|
||||
# Verify that encrypt() uses the custom MGF
|
||||
global mgfcalls
|
||||
# Helper function to monitor what's requested from MGF
|
||||
|
||||
def newMGF(seed, maskLen):
|
||||
global mgfcalls
|
||||
mgfcalls += 1
|
||||
return b'\x00' * maskLen
|
||||
|
||||
mgfcalls = 0
|
||||
pt = self.rng(32)
|
||||
cipher = PKCS.new(self.key1024, mgfunc=newMGF)
|
||||
ct = cipher.encrypt(pt)
|
||||
self.assertEqual(mgfcalls, 2)
|
||||
self.assertEqual(cipher.decrypt(ct), pt)
|
||||
|
||||
def testByteArray(self):
|
||||
pt = b("XER")
|
||||
cipher = PKCS.new(self.key1024)
|
||||
ct = cipher.encrypt(bytearray(pt))
|
||||
pt2 = cipher.decrypt(bytearray(ct))
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
def testMemoryview(self):
|
||||
pt = b("XER")
|
||||
cipher = PKCS.new(self.key1024)
|
||||
ct = cipher.encrypt(memoryview(bytearray(pt)))
|
||||
pt2 = cipher.decrypt(memoryview(bytearray(ct)))
|
||||
self.assertEqual(pt, pt2)
|
||||
|
||||
|
||||
class TestVectorsWycheproof(unittest.TestCase):
|
||||
|
||||
def __init__(self, wycheproof_warnings, skip_slow_tests):
|
||||
unittest.TestCase.__init__(self)
|
||||
self._wycheproof_warnings = wycheproof_warnings
|
||||
self._skip_slow_tests = skip_slow_tests
|
||||
self._id = "None"
|
||||
|
||||
def load_tests(self, filename):
|
||||
|
||||
def filter_rsa(group):
|
||||
return RSA.import_key(group['privateKeyPem'])
|
||||
|
||||
def filter_sha(group):
|
||||
if group['sha'] == "SHA-1":
|
||||
return SHA1
|
||||
elif group['sha'] == "SHA-224":
|
||||
return SHA224
|
||||
elif group['sha'] == "SHA-256":
|
||||
return SHA256
|
||||
elif group['sha'] == "SHA-384":
|
||||
return SHA384
|
||||
elif group['sha'] == "SHA-512":
|
||||
return SHA512
|
||||
else:
|
||||
raise ValueError("Unknown sha " + group['sha'])
|
||||
|
||||
def filter_mgf(group):
|
||||
if group['mgfSha'] == "SHA-1":
|
||||
return lambda x, y: MGF1(x, y, SHA1)
|
||||
elif group['mgfSha'] == "SHA-224":
|
||||
return lambda x, y: MGF1(x, y, SHA224)
|
||||
elif group['mgfSha'] == "SHA-256":
|
||||
return lambda x, y: MGF1(x, y, SHA256)
|
||||
elif group['mgfSha'] == "SHA-384":
|
||||
return lambda x, y: MGF1(x, y, SHA384)
|
||||
elif group['mgfSha'] == "SHA-512":
|
||||
return lambda x, y: MGF1(x, y, SHA512)
|
||||
else:
|
||||
raise ValueError("Unknown mgf/sha " + group['mgfSha'])
|
||||
|
||||
def filter_algo(group):
|
||||
return "%s with MGF1/%s" % (group['sha'], group['mgfSha'])
|
||||
|
||||
result = load_test_vectors_wycheproof(("Cipher", "wycheproof"),
|
||||
filename,
|
||||
"Wycheproof PKCS#1 OAEP (%s)" % filename,
|
||||
group_tag={'rsa_key': filter_rsa,
|
||||
'hash_mod': filter_sha,
|
||||
'mgf': filter_mgf,
|
||||
'algo': filter_algo}
|
||||
)
|
||||
return result
|
||||
|
||||
def setUp(self):
|
||||
self.tv = []
|
||||
self.tv.extend(self.load_tests("rsa_oaep_2048_sha1_mgf1sha1_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_2048_sha224_mgf1sha1_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_2048_sha224_mgf1sha224_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_2048_sha256_mgf1sha1_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_2048_sha256_mgf1sha256_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_2048_sha384_mgf1sha1_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_2048_sha384_mgf1sha384_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_2048_sha512_mgf1sha1_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_2048_sha512_mgf1sha512_test.json"))
|
||||
if not self._skip_slow_tests:
|
||||
self.tv.extend(self.load_tests("rsa_oaep_3072_sha256_mgf1sha1_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_3072_sha256_mgf1sha256_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_3072_sha512_mgf1sha1_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_3072_sha512_mgf1sha512_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_4096_sha256_mgf1sha1_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_4096_sha256_mgf1sha256_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_4096_sha512_mgf1sha1_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_4096_sha512_mgf1sha512_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_4096_sha512_mgf1sha512_test.json"))
|
||||
self.tv.extend(self.load_tests("rsa_oaep_misc_test.json"))
|
||||
|
||||
def shortDescription(self):
|
||||
return self._id
|
||||
|
||||
def warn(self, tv):
|
||||
if tv.warning and self._wycheproof_warnings:
|
||||
import warnings
|
||||
warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment))
|
||||
|
||||
def test_decrypt(self, tv):
|
||||
self._id = "Wycheproof Decrypt %s Test #%s" % (tv.algo, tv.id)
|
||||
|
||||
cipher = PKCS.new(tv.rsa_key, hashAlgo=tv.hash_mod, mgfunc=tv.mgf, label=tv.label)
|
||||
try:
|
||||
pt = cipher.decrypt(tv.ct)
|
||||
except ValueError:
|
||||
assert not tv.valid
|
||||
else:
|
||||
assert tv.valid
|
||||
self.assertEqual(pt, tv.msg)
|
||||
self.warn(tv)
|
||||
|
||||
def runTest(self):
|
||||
|
||||
for tv in self.tv:
|
||||
self.test_decrypt(tv)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
skip_slow_tests = not config.get('slow_tests')
|
||||
wycheproof_warnings = config.get('wycheproof_warnings')
|
||||
|
||||
tests = []
|
||||
tests += list_test_cases(PKCS1_OAEP_Tests)
|
||||
tests += [TestVectorsWycheproof(wycheproof_warnings, skip_slow_tests)]
|
||||
return tests
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def suite():
|
||||
unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
Reference in New Issue
Block a user