summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLegrandin <helderijs@gmail.com>2013-05-13 08:40:46 +0200
committerDwayne Litzenberger <dlitz@dlitz.net>2013-10-20 13:30:21 -0700
commitda79b781af41ff815b812c49d9be434f5de52aa4 (patch)
tree16c3d6251bfafaeb7bd96743d2be3e5d4fbec439
parent92fea1b6065c6ca75381b465f846843494372c4d (diff)
downloadpycrypto-da79b781af41ff815b812c49d9be434f5de52aa4.tar.gz
Add support for CMAC
This patch adds support for CMAC (RFC4493, NIST SP800-38B). [dlitz@dlitz.net: Replaced MacMismatchError with ValueError] [dlitz@dlitz.net: Whitespace fixed with "git rebase --whitespace=fix"]
-rw-r--r--lib/Crypto/Hash/CMAC.py277
-rw-r--r--lib/Crypto/Hash/__init__.py3
-rw-r--r--lib/Crypto/SelfTest/Hash/__init__.py1
-rw-r--r--lib/Crypto/SelfTest/Hash/test_CMAC.py249
-rw-r--r--pct-speedtest.py20
5 files changed, 548 insertions, 2 deletions
diff --git a/lib/Crypto/Hash/CMAC.py b/lib/Crypto/Hash/CMAC.py
new file mode 100644
index 0000000..1e3cf53
--- /dev/null
+++ b/lib/Crypto/Hash/CMAC.py
@@ -0,0 +1,277 @@
+# -*- coding: utf-8 -*-
+#
+# Hash/CMAC.py - Implements the CMAC algorithm
+#
+# ===================================================================
+# 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.
+# ===================================================================
+
+"""CMAC (Cipher-based Message Authentication Code) algorithm
+
+CMAC is a MAC defined in `NIST SP 800-38B`_ and in RFC4493_ (for AES only)
+and constructed using a block cipher. It was originally known as `OMAC1`_.
+
+The algorithm is sometimes named *X-CMAC* where *X* is the name
+of the cipher (e.g. AES-CMAC).
+
+This is an example showing how to *create* an AES-CMAC:
+
+ >>> from Crypto.Hash import CMAC
+ >>> from Crypto.Cipher import AES
+ >>>
+ >>> secret = b'Sixteen byte key'
+ >>> cobj = CMAC.new(secret, ciphermod=AES)
+ >>> cobj.update(b'Hello')
+ >>> print cobj.hexdigest()
+
+And this is an example showing how to *check* an AES-CMAC:
+
+ >>> from Crypto.Hash import CMAC
+ >>> from Crypto.Cipher import AES
+ >>>
+ >>> # We have received a message 'msg' together
+ >>> # with its MAC 'mac'
+ >>>
+ >>> secret = b'Sixteen byte key'
+ >>> cobj = CMAC.new(secret, ciphermod=AES)
+ >>> cobj.update(msg)
+ >>> try:
+ >>> cobj.verify(mac)
+ >>> print "The message '%s' is authentic" % msg
+ >>> except ValueError:
+ >>> print "The message or the key is wrong"
+
+.. _`NIST SP 800-38B`: http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf
+.. _RFC4493: http://www.ietf.org/rfc/rfc4493.txt
+.. _OMAC1: http://www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
+"""
+
+__all__ = ['new', 'digest_size', 'CMAC' ]
+
+import sys
+if sys.version_info[0] == 2 and sys.version_info[1] == 1:
+ from Crypto.Util.py21compat import *
+from Crypto.Util.py3compat import *
+
+from binascii import unhexlify
+
+from Crypto.Util.strxor import strxor
+from Crypto.Util.number import long_to_bytes, bytes_to_long
+
+#: The size of the authentication tag produced by the MAC.
+digest_size = None
+
+def _shift_bytes(bs, xor_lsb=0):
+ num = (bytes_to_long(bs)<<1) ^ xor_lsb
+ return long_to_bytes(num, len(bs))[-len(bs):]
+
+class CMAC(object):
+ """Class that implements CMAC"""
+
+ #: The size of the authentication tag produced by the MAC.
+ digest_size = None
+
+ def __init__(self, key, msg = None, ciphermod = None):
+ """Create a new CMAC object.
+
+ :Parameters:
+ key : byte string
+ secret key for the CMAC object.
+ The key must be valid for the underlying cipher algorithm.
+ For instance, it must be 16 bytes long for AES-128.
+ msg : byte string
+ The very first chunk of the message to authenticate.
+ It is equivalent to an early call to `update`. Optional.
+ ciphermod : module
+ A cipher module from `Crypto.Cipher`.
+ The cipher's block size must be 64 or 128 bits.
+ It is recommended to use `Crypto.Cipher.AES`.
+ """
+
+ if ciphermod is None:
+ raise ValueError("ciphermod must be specified (try AES)")
+
+ self._key = key
+ self._factory = ciphermod
+
+ # Section 5.3 of NIST SP 800 38B
+ if ciphermod.block_size==8:
+ const_Rb = 0x1B
+ elif ciphermod.block_size==16:
+ const_Rb = 0x87
+ else:
+ raise ValueError("For CMAC, block length of the selected cipher must be 8 or 16 bytes")
+ self.digest_size = ciphermod.block_size
+
+ # MAC cache
+ self._tag = None
+
+ # Compute sub-keys
+ cipher = ciphermod.new(key, ciphermod.MODE_ECB)
+ l = cipher.encrypt(bchr(0)*ciphermod.block_size)
+ if bord(l[0]) & 0x80:
+ self._k1 = _shift_bytes(l, const_Rb)
+ else:
+ self._k1 = _shift_bytes(l)
+ if bord(self._k1[0]) & 0x80:
+ self._k2 = _shift_bytes(self._k1, const_Rb)
+ else:
+ self._k2 = _shift_bytes(self._k1)
+
+ # Initialize CBC cipher with zero IV
+ self._IV = bchr(0)*ciphermod.block_size
+ self._cipherCBC = ciphermod.new(key, ciphermod.MODE_CBC, self._IV)
+
+ self._buffer = []
+ self._buffer_len = 0
+
+ if msg is not None:
+ self.update(msg)
+
+ def update(self, msg):
+ """Continue authentication of a message by consuming the next chunk of data.
+
+ Repeated calls are equivalent to a single call with the concatenation
+ of all the arguments. In other words:
+
+ >>> m.update(a); m.update(b)
+
+ is equivalent to:
+
+ >>> m.update(a+b)
+
+ :Parameters:
+ msg : byte string
+ The next chunk of the message being authenticated
+ """
+
+ self._buffer += [ msg ]
+ self._buffer_len += len(msg)
+
+ # MAC data as you go but leave at least 1 byte in the buffer
+ bsize = self._cipherCBC.block_size
+ if self._buffer_len>bsize:
+ data = b("").join(self._buffer)
+ self._buffer_len = self._buffer_len&(bsize-1)
+ if self._buffer_len==0:
+ self._buffer_len=bsize
+ self._buffer = [ data[-self._buffer_len:] ]
+ self._IV = self._cipherCBC.encrypt(data[:-self._buffer_len])[-bsize:]
+
+ def copy(self):
+ """Return a copy ("clone") of the MAC object.
+
+ The copy will have the same internal state as the original MAC
+ object.
+ This can be used to efficiently compute the MAC of strings that
+ share a common initial substring.
+
+ :Returns: A `CMAC` object
+ """
+ obj = CMAC(self._key, ciphermod=self._factory)
+
+ # Deep copy
+ for m in [ '_tag', '_buffer', '_buffer_len', '_k1', '_k2', '_IV']:
+ setattr(obj, m, getattr(self, m))
+ obj._cipherCBC = self._factory.new(self._key, self._factory.MODE_CBC, self._IV)
+ return obj
+
+ def digest(self):
+ """Return the **binary** (non-printable) MAC of the message that has
+ been authenticated so far.
+
+ This method does not change the state of the MAC object.
+ You can continue updating the object after calling this function.
+
+ :Return: A byte string of `digest_size` bytes. It may contain non-ASCII
+ characters, including null bytes.
+ """
+
+ if not self._tag:
+ data = b("").join(self._buffer)
+ bsize = self._cipherCBC.block_size
+ if len(data)==bsize:
+ last_block = strxor(data, self._k1)
+ else:
+ last_block = strxor(data+bchr(128)+bchr(0)*(bsize-1-len(data)), self._k2)
+ self._tag = self._cipherCBC.encrypt(last_block)
+
+ return self._tag
+
+ def hexdigest(self):
+ """Return the **printable** MAC of the message that has been
+ authenticated so far.
+
+ This method does not change the state of the MAC object.
+
+ :Return: A string of 2* `digest_size` bytes. It contains only
+ hexadecimal ASCII digits.
+ """
+ return "".join(["%02x" % bord(x)
+ for x in tuple(self.digest())])
+
+ def verify(self, mac_tag):
+ """Verify that a given **binary** MAC (computed by another party) is valid.
+
+ :Parameters:
+ mac_tag : byte string
+ The expected MAC of the message.
+ :Raises ValueError:
+ if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ mac = self.digest()
+ res = 0
+ # Constant-time comparison
+ for x,y in zip(mac, mac_tag):
+ res |= bord(x) ^ bord(y)
+ if res or len(mac_tag)!=self.digest_size:
+ raise ValueError("MAC check failed")
+
+ def hexverify(self, hex_mac_tag):
+ """Verify that a given **printable** MAC (computed by another party) is valid.
+
+ :Parameters:
+ hex_mac_tag : string
+ The expected MAC of the message, as a hexadecimal string.
+ :Raises ValueError:
+ if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ self.verify(unhexlify(hex_mac_tag))
+
+def new(key, msg = None, ciphermod = None):
+ """Create a new CMAC object.
+
+ :Parameters:
+ key : byte string
+ secret key for the CMAC object.
+ The key must be valid for the underlying cipher algorithm.
+ For instance, it must be 16 bytes long for AES-128.
+ msg : byte string
+ The very first chunk of the message to authenticate.
+ It is equivalent to an early call to `CMAC.update`. Optional.
+ ciphermod : module
+ A cipher module from `Crypto.Cipher`.
+ The cipher's block size must be 64 or 128 bits.
+ Default is `Crypto.Cipher.AES`.
+
+ :Returns: A `CMAC` object
+ """
+ return CMAC(key, msg, ciphermod)
diff --git a/lib/Crypto/Hash/__init__.py b/lib/Crypto/Hash/__init__.py
index 1050c78..f16e253 100644
--- a/lib/Crypto/Hash/__init__.py
+++ b/lib/Crypto/Hash/__init__.py
@@ -50,7 +50,8 @@ The hashing modules here all support the interface described in `PEP
"""
__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD160', 'SHA1',
- 'SHA224', 'SHA256', 'SHA384', 'SHA512']
+ 'SHA224', 'SHA256', 'SHA384', 'SHA512', 'CMAC']
+
__revision__ = "$Id$"
import sys
diff --git a/lib/Crypto/SelfTest/Hash/__init__.py b/lib/Crypto/SelfTest/Hash/__init__.py
index d6c8e57..e2d9cd8 100644
--- a/lib/Crypto/SelfTest/Hash/__init__.py
+++ b/lib/Crypto/SelfTest/Hash/__init__.py
@@ -29,6 +29,7 @@ __revision__ = "$Id$"
def get_tests(config={}):
tests = []
from Crypto.SelfTest.Hash import test_HMAC; tests += test_HMAC.get_tests(config=config)
+ from Crypto.SelfTest.Hash import test_CMAC; tests += test_CMAC.get_tests(config=config)
from Crypto.SelfTest.Hash import test_MD2; tests += test_MD2.get_tests(config=config)
from Crypto.SelfTest.Hash import test_MD4; tests += test_MD4.get_tests(config=config)
from Crypto.SelfTest.Hash import test_MD5; tests += test_MD5.get_tests(config=config)
diff --git a/lib/Crypto/SelfTest/Hash/test_CMAC.py b/lib/Crypto/SelfTest/Hash/test_CMAC.py
new file mode 100644
index 0000000..f16d89f
--- /dev/null
+++ b/lib/Crypto/SelfTest/Hash/test_CMAC.py
@@ -0,0 +1,249 @@
+# -*- coding: utf-8 -*-
+#
+# SelfTest/Hash/CMAC.py: Self-test for the CMAC module
+#
+# ===================================================================
+# 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.Hash.CMAC"""
+
+import sys
+if sys.version_info[0] == 2 and sys.version_info[1] == 1:
+ from Crypto.Util.py21compat import *
+from Crypto.Util.py3compat import *
+
+from common import dict
+
+from Crypto.Hash import CMAC
+from Crypto.Cipher import AES, DES3
+
+# This is a list of (key, data, result, description, module) tuples.
+test_data = [
+
+ ## Test vectors from RFC 4493 ##
+ ## The are also in NIST SP 800 38B D.2 ##
+ ( '2b7e151628aed2a6abf7158809cf4f3c',
+ '',
+ 'bb1d6929e95937287fa37d129b756746',
+ 'RFC 4493 #1',
+ AES
+ ),
+
+ ( '2b7e151628aed2a6abf7158809cf4f3c',
+ '6bc1bee22e409f96e93d7e117393172a',
+ '070a16b46b4d4144f79bdd9dd04a287c',
+ 'RFC 4493 #2',
+ AES
+ ),
+
+ ( '2b7e151628aed2a6abf7158809cf4f3c',
+ '6bc1bee22e409f96e93d7e117393172a'+
+ 'ae2d8a571e03ac9c9eb76fac45af8e51'+
+ '30c81c46a35ce411',
+ 'dfa66747de9ae63030ca32611497c827',
+ 'RFC 4493 #3',
+ AES
+ ),
+
+ ( '2b7e151628aed2a6abf7158809cf4f3c',
+ '6bc1bee22e409f96e93d7e117393172a'+
+ 'ae2d8a571e03ac9c9eb76fac45af8e51'+
+ '30c81c46a35ce411e5fbc1191a0a52ef'+
+ 'f69f2445df4f9b17ad2b417be66c3710',
+ '51f0bebf7e3b9d92fc49741779363cfe',
+ 'RFC 4493 #4',
+ AES
+ ),
+
+ ## The rest of Appendix D of NIST SP 800 38B
+ ## was not totally correct.
+ ## Values in Examples 14, 15, 18, and 19 were wrong.
+ ## The updated test values are published in:
+ ## http://csrc.nist.gov/publications/nistpubs/800-38B/Updated_CMAC_Examples.pdf
+
+ ( '8e73b0f7da0e6452c810f32b809079e5'+
+ '62f8ead2522c6b7b',
+ '',
+ 'd17ddf46adaacde531cac483de7a9367',
+ 'NIST SP 800 38B D.2 Example 5',
+ AES
+ ),
+
+ ( '8e73b0f7da0e6452c810f32b809079e5'+
+ '62f8ead2522c6b7b',
+ '6bc1bee22e409f96e93d7e117393172a',
+ '9e99a7bf31e710900662f65e617c5184',
+ 'NIST SP 800 38B D.2 Example 6',
+ AES
+ ),
+
+ ( '8e73b0f7da0e6452c810f32b809079e5'+
+ '62f8ead2522c6b7b',
+ '6bc1bee22e409f96e93d7e117393172a'+
+ 'ae2d8a571e03ac9c9eb76fac45af8e51'+
+ '30c81c46a35ce411',
+ '8a1de5be2eb31aad089a82e6ee908b0e',
+ 'NIST SP 800 38B D.2 Example 7',
+ AES
+ ),
+
+ ( '8e73b0f7da0e6452c810f32b809079e5'+
+ '62f8ead2522c6b7b',
+ '6bc1bee22e409f96e93d7e117393172a'+
+ 'ae2d8a571e03ac9c9eb76fac45af8e51'+
+ '30c81c46a35ce411e5fbc1191a0a52ef'+
+ 'f69f2445df4f9b17ad2b417be66c3710',
+ 'a1d5df0eed790f794d77589659f39a11',
+ 'NIST SP 800 38B D.2 Example 8',
+ AES
+ ),
+
+ ( '603deb1015ca71be2b73aef0857d7781'+
+ '1f352c073b6108d72d9810a30914dff4',
+ '',
+ '028962f61b7bf89efc6b551f4667d983',
+ 'NIST SP 800 38B D.3 Example 9',
+ AES
+ ),
+
+ ( '603deb1015ca71be2b73aef0857d7781'+
+ '1f352c073b6108d72d9810a30914dff4',
+ '6bc1bee22e409f96e93d7e117393172a',
+ '28a7023f452e8f82bd4bf28d8c37c35c',
+ 'NIST SP 800 38B D.3 Example 10',
+ AES
+ ),
+
+ ( '603deb1015ca71be2b73aef0857d7781'+
+ '1f352c073b6108d72d9810a30914dff4',
+ '6bc1bee22e409f96e93d7e117393172a'+
+ 'ae2d8a571e03ac9c9eb76fac45af8e51'+
+ '30c81c46a35ce411',
+ 'aaf3d8f1de5640c232f5b169b9c911e6',
+ 'NIST SP 800 38B D.3 Example 11',
+ AES
+ ),
+
+ ( '603deb1015ca71be2b73aef0857d7781'+
+ '1f352c073b6108d72d9810a30914dff4',
+ '6bc1bee22e409f96e93d7e117393172a'+
+ 'ae2d8a571e03ac9c9eb76fac45af8e51'+
+ '30c81c46a35ce411e5fbc1191a0a52ef'+
+ 'f69f2445df4f9b17ad2b417be66c3710',
+ 'e1992190549f6ed5696a2c056c315410',
+ 'NIST SP 800 38B D.3 Example 12',
+ AES
+ ),
+
+ ( '8aa83bf8cbda1062'+
+ '0bc1bf19fbb6cd58'+
+ 'bc313d4a371ca8b5',
+ '',
+ 'b7a688e122ffaf95',
+ 'NIST SP 800 38B D.4 Example 13',
+ DES3
+ ),
+
+ ( '8aa83bf8cbda1062'+
+ '0bc1bf19fbb6cd58'+
+ 'bc313d4a371ca8b5',
+ '6bc1bee22e409f96',
+ '8e8f293136283797',
+ 'NIST SP 800 38B D.4 Example 14',
+ DES3
+ ),
+
+ ( '8aa83bf8cbda1062'+
+ '0bc1bf19fbb6cd58'+
+ 'bc313d4a371ca8b5',
+ '6bc1bee22e409f96'+
+ 'e93d7e117393172a'+
+ 'ae2d8a57',
+ '743ddbe0ce2dc2ed',
+ 'NIST SP 800 38B D.4 Example 15',
+ DES3
+ ),
+
+ ( '8aa83bf8cbda1062'+
+ '0bc1bf19fbb6cd58'+
+ 'bc313d4a371ca8b5',
+ '6bc1bee22e409f96'+
+ 'e93d7e117393172a'+
+ 'ae2d8a571e03ac9c'+
+ '9eb76fac45af8e51',
+ '33e6b1092400eae5',
+ 'NIST SP 800 38B D.4 Example 16',
+ DES3
+ ),
+
+ ( '4cf15134a2850dd5'+
+ '8a3d10ba80570d38',
+ '',
+ 'bd2ebf9a3ba00361',
+ 'NIST SP 800 38B D.7 Example 17',
+ DES3
+ ),
+
+ ( '4cf15134a2850dd5'+
+ '8a3d10ba80570d38',
+ '6bc1bee22e409f96',
+ '4ff2ab813c53ce83',
+ 'NIST SP 800 38B D.7 Example 18',
+ DES3
+ ),
+
+ ( '4cf15134a2850dd5'+
+ '8a3d10ba80570d38',
+ '6bc1bee22e409f96'+
+ 'e93d7e117393172a'+
+ 'ae2d8a57',
+ '62dd1b471902bd4e',
+ 'NIST SP 800 38B D.7 Example 19',
+ DES3
+ ),
+
+ ( '4cf15134a2850dd5'+
+ '8a3d10ba80570d38',
+ '6bc1bee22e409f96'+
+ 'e93d7e117393172a'+
+ 'ae2d8a571e03ac9c'+
+ '9eb76fac45af8e51',
+ '31b1e431dabc4eb8',
+ 'NIST SP 800 38B D.7 Example 20',
+ DES3
+ ),
+
+]
+
+def get_tests(config={}):
+ global test_data
+ from common import make_mac_tests
+
+ # Add new() parameters to the back of each test vector
+ params_test_data = []
+ for row in test_data:
+ t = list(row)
+ t[4] = dict(ciphermod=t[4])
+ params_test_data.append(t)
+
+ return make_mac_tests(CMAC, "CMAC", params_test_data)
+
+if __name__ == '__main__':
+ import unittest
+ suite = lambda: unittest.TestSuite(get_tests())
+ unittest.main(defaultTest='suite')
diff --git a/pct-speedtest.py b/pct-speedtest.py
index 365eca2..e84cf81 100644
--- a/pct-speedtest.py
+++ b/pct-speedtest.py
@@ -31,7 +31,7 @@ from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP, PKCS1_v1_5 as RSAES_PKCS1_v1_5
from Crypto.Signature import PKCS1_PSS, PKCS1_v1_5 as RSASSA_PKCS1_v1_5
from Crypto.Cipher import AES, ARC2, ARC4, Blowfish, CAST, DES3, DES, XOR
-from Crypto.Hash import HMAC, MD2, MD4, MD5, SHA224, SHA256, SHA384, SHA512
+from Crypto.Hash import HMAC, MD2, MD4, MD5, SHA224, SHA256, SHA384, SHA512, CMAC
from Crypto.Random import get_random_bytes
import Crypto.Util.Counter
from Crypto.Util.number import bytes_to_long
@@ -227,6 +227,19 @@ class Benchmark:
mac_constructor = lambda data=None: hmac_constructor(key, data, digestmod)
self.test_hash_large(mac_name, mac_constructor, digest_size)
+ def test_cmac_small(self, mac_name, cmac_constructor, ciphermod, key_size):
+ keys = iter(self.random_keys(key_size))
+ if sys.version_info[0] == 2:
+ mac_constructor = lambda data=None: cmac_constructor(keys.next(), data, ciphermod)
+ else:
+ mac_constructor = lambda data=None: cmac_constructor(keys.__next__(), data, ciphermod)
+ self.test_hash_small(mac_name, mac_constructor, ciphermod.block_size)
+
+ def test_cmac_large(self, mac_name, cmac_constructor, ciphermod, key_size):
+ key = self.random_keys(key_size)[0]
+ mac_constructor = lambda data=None: cmac_constructor(key, data, ciphermod)
+ self.test_hash_large(mac_name, mac_constructor, ciphermod.block_size)
+
def test_pkcs1_sign(self, scheme_name, scheme_constructor, hash_name, hash_constructor, digest_size):
self.announce_start("%s signing %s (%d-byte inputs)" % (scheme_name, hash_name, digest_size))
@@ -372,6 +385,11 @@ class Benchmark:
self.test_hmac_small("hmac+"+hash_name, hmac.HMAC, func, func().digest_size)
self.test_hmac_large("hmac+"+hash_name, hmac.HMAC, func, func().digest_size)
+ # CMAC
+ for cipher_name, module, key_size in (("AES128", AES, 16),):
+ self.test_cmac_small(cipher_name+"-CMAC", CMAC.new, module, key_size)
+ self.test_cmac_large(cipher_name+"-CMAC", CMAC.new, module, key_size)
+
# PKCS1_v1_5 (sign) + Crypto.Hash
for hash_name, module in hash_specs:
self.test_pkcs1_sign("PKCS#1-v1.5", RSASSA_PKCS1_v1_5.new, hash_name, module.new, module.digest_size)