slight update

This commit is contained in:
2024-12-01 04:19:04 +09:00
parent 00b0afd17a
commit 4dbe1bee11
3130 changed files with 508084 additions and 0 deletions

View File

@ -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:

View 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:

File diff suppressed because it is too large Load Diff

View File

@ -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:

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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:

View File

@ -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:

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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:

View File

@ -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:

View File

@ -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: