summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLegrandin <helderijs@gmail.com>2013-05-20 12:43:44 +0200
committerDwayne Litzenberger <dlitz@dlitz.net>2013-10-20 13:30:21 -0700
commit8bdbdb8168511018d44ef014ae21da619ae73c24 (patch)
tree75d7a4c60d07dc0bb960705e71ac5f551d8631a9
parent57104488faa9fc386ea1aee249bafb6e2a529a57 (diff)
downloadpycrypto-8bdbdb8168511018d44ef014ae21da619ae73c24.tar.gz
Add EAX authenticated encryption mode
[dlitz@dlitz.net: Included changes from the following commits from the author's pull request:] - [9c13f9c] Rename 'IV' parameter to 'nonce' for AEAD modes. - [ca460a7] Made blockalgo.py more PEP-8 compliant; The second parameter of the _GHASH constructor is now the length of the block (block_size) and not the full module. [dlitz@dlitz.net: Fixed unresolved conflict in lib/Crypto/Cipher/blockalgo.py]
-rw-r--r--lib/Crypto/Cipher/AES.py10
-rw-r--r--lib/Crypto/Cipher/ARC2.py16
-rw-r--r--lib/Crypto/Cipher/Blowfish.py16
-rw-r--r--lib/Crypto/Cipher/CAST.py16
-rw-r--r--lib/Crypto/Cipher/DES.py16
-rw-r--r--lib/Crypto/Cipher/DES3.py18
-rw-r--r--lib/Crypto/Cipher/blockalgo.py171
-rw-r--r--lib/Crypto/SelfTest/Cipher/common.py18
-rw-r--r--lib/Crypto/SelfTest/Cipher/test_AES.py80
-rw-r--r--pct-speedtest.py2
10 files changed, 306 insertions, 57 deletions
diff --git a/lib/Crypto/Cipher/AES.py b/lib/Crypto/Cipher/AES.py
index 5316700..5cbd04b 100644
--- a/lib/Crypto/Cipher/AES.py
+++ b/lib/Crypto/Cipher/AES.py
@@ -138,7 +138,7 @@ def new(key, *args, **kwargs):
For all other modes, it must be 16 bytes long.
nonce : byte string
- (*Only* `MODE_CCM`).
+ (*Only* `MODE_CCM`, `MODE_EAX`).
A mandatory value that must never be reused for any other encryption.
@@ -146,6 +146,9 @@ def new(key, *args, **kwargs):
11 or 12 bytes are reasonable values in general. Bear in
mind that with CCM there is a trade-off between nonce length and
maximum message size.
+
+ For the other modes, there are no restrictions on its length,
+ but it is recommended to use at least 16 bytes.
counter : callable
(*Only* `MODE_CTR`). A stateful function that returns the next
*counter block*, which is a byte string of `block_size` bytes.
@@ -157,6 +160,9 @@ def new(key, *args, **kwargs):
mac_len : integer
(*Only* `MODE_CCM`). Length of the MAC, in bytes. It must be even and in
the range ``[4..16]``. The default is 16.
+
+ (*Only* `MODE_EAX`). Length of the MAC, in bytes. It must be no
+ larger than 16 bytes (which is the default).
msg_len : integer
(*Only* `MODE_CCM`). Length of the message to (de)cipher.
If not specified, ``encrypt`` or ``decrypt`` may only be called once.
@@ -186,6 +192,8 @@ MODE_CTR = 6
MODE_OPENPGP = 7
#: Counter with CBC-MAC (CCM) Mode. See `blockalgo.MODE_CCM`.
MODE_CCM = 8
+#: EAX Mode. See `blockalgo.MODE_EAX`.
+MODE_EAX = 9
#: Size of a data block (in bytes)
block_size = 16
#: Size of a key (in bytes)
diff --git a/lib/Crypto/Cipher/ARC2.py b/lib/Crypto/Cipher/ARC2.py
index 1b62be1..1b3fa4e 100644
--- a/lib/Crypto/Cipher/ARC2.py
+++ b/lib/Crypto/Cipher/ARC2.py
@@ -82,6 +82,8 @@ def new(key, *args, **kwargs):
The chaining mode to use for encryption or decryption.
Default is `MODE_ECB`.
IV : byte string
+ (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
+
The initialization vector to use for encryption or decryption.
It is ignored for `MODE_ECB` and `MODE_CTR`.
@@ -90,12 +92,20 @@ def new(key, *args, **kwargs):
and `block_size` +2 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
It is mandatory.
-
- For all other modes, it must be `block_size` bytes longs.
+
+ For all other modes, it must be 8 bytes long.
+ nonce : byte string
+ (*Only* `MODE_EAX`).
+ A mandatory value that must never be reused for any other encryption.
+ There are no restrictions on its length, but it is recommended to
+ use at least 16 bytes.
counter : callable
(*Only* `MODE_CTR`). A stateful function that returns the next
*counter block*, which is a byte string of `block_size` bytes.
For better performance, use `Crypto.Util.Counter`.
+ mac_len : integer
+ (*Only* `MODE_EAX`). Length of the MAC, in bytes.
+ It must be no larger than 8 (which is the default).
segment_size : integer
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in.
@@ -122,6 +132,8 @@ MODE_OFB = 5
MODE_CTR = 6
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
MODE_OPENPGP = 7
+#: EAX Mode. See `blockalgo.MODE_EAX`.
+MODE_EAX = 9
#: Size of a data block (in bytes)
block_size = 8
#: Size of a key (in bytes)
diff --git a/lib/Crypto/Cipher/Blowfish.py b/lib/Crypto/Cipher/Blowfish.py
index 5c3128d..92fad7f 100644
--- a/lib/Crypto/Cipher/Blowfish.py
+++ b/lib/Crypto/Cipher/Blowfish.py
@@ -76,6 +76,8 @@ def new(key, *args, **kwargs):
The chaining mode to use for encryption or decryption.
Default is `MODE_ECB`.
IV : byte string
+ (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
+
The initialization vector to use for encryption or decryption.
It is ignored for `MODE_ECB` and `MODE_CTR`.
@@ -84,12 +86,20 @@ def new(key, *args, **kwargs):
and `block_size` +2 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
It is mandatory.
-
- For all other modes, it must be `block_size` bytes longs.
+
+ For all other modes, it must be 8 bytes long.
+ nonce : byte string
+ (*Only* `MODE_EAX`).
+ A mandatory value that must never be reused for any other encryption.
+ There are no restrictions on its length, but it is recommended to
+ use at least 16 bytes.
counter : callable
(*Only* `MODE_CTR`). A stateful function that returns the next
*counter block*, which is a byte string of `block_size` bytes.
For better performance, use `Crypto.Util.Counter`.
+ mac_len : integer
+ (*Only* `MODE_EAX`). Length of the MAC, in bytes.
+ It must be no larger than 8 (which is the default).
segment_size : integer
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in.
@@ -113,6 +123,8 @@ MODE_OFB = 5
MODE_CTR = 6
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
MODE_OPENPGP = 7
+#: EAX Mode. See `blockalgo.MODE_EAX`.
+MODE_EAX = 9
#: Size of a data block (in bytes)
block_size = 8
#: Size of a key (in bytes)
diff --git a/lib/Crypto/Cipher/CAST.py b/lib/Crypto/Cipher/CAST.py
index 88f8cac..6e136a3 100644
--- a/lib/Crypto/Cipher/CAST.py
+++ b/lib/Crypto/Cipher/CAST.py
@@ -79,6 +79,8 @@ def new(key, *args, **kwargs):
The chaining mode to use for encryption or decryption.
Default is `MODE_ECB`.
IV : byte string
+ (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
+
The initialization vector to use for encryption or decryption.
It is ignored for `MODE_ECB` and `MODE_CTR`.
@@ -87,12 +89,20 @@ def new(key, *args, **kwargs):
and `block_size` +2 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
It is mandatory.
-
- For all other modes, it must be `block_size` bytes longs.
+
+ For all other modes, it must be 8 bytes long.
+ nonce : byte string
+ (*Only* `MODE_EAX`).
+ A mandatory value that must never be reused for any other encryption.
+ There are no restrictions on its length, but it is recommended to
+ use at least 16 bytes.
counter : callable
(*Only* `MODE_CTR`). A stateful function that returns the next
*counter block*, which is a byte string of `block_size` bytes.
For better performance, use `Crypto.Util.Counter`.
+ mac_len : integer
+ (*Only* `MODE_EAX`). Length of the MAC, in bytes.
+ It must be no larger than 8 (which is the default).
segment_size : integer
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in.
@@ -116,6 +126,8 @@ MODE_OFB = 5
MODE_CTR = 6
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
MODE_OPENPGP = 7
+#: EAX Mode. See `blockalgo.MODE_EAX`.
+MODE_EAX = 9
#: Size of a data block (in bytes)
block_size = 8
#: Size of a key (in bytes)
diff --git a/lib/Crypto/Cipher/DES.py b/lib/Crypto/Cipher/DES.py
index 2ae63bc..18a7bd1 100644
--- a/lib/Crypto/Cipher/DES.py
+++ b/lib/Crypto/Cipher/DES.py
@@ -74,6 +74,8 @@ def new(key, *args, **kwargs):
The chaining mode to use for encryption or decryption.
Default is `MODE_ECB`.
IV : byte string
+ (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
+
The initialization vector to use for encryption or decryption.
It is ignored for `MODE_ECB` and `MODE_CTR`.
@@ -82,12 +84,20 @@ def new(key, *args, **kwargs):
and `block_size` +2 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
It is mandatory.
-
- For all other modes, it must be `block_size` bytes longs.
+
+ For all other modes, it must be 8 bytes long.
+ nonce : byte string
+ (*Only* `MODE_EAX`).
+ A mandatory value that must never be reused for any other encryption.
+ There are no restrictions on its length, but it is recommended to
+ use at least 16 bytes.
counter : callable
(*Only* `MODE_CTR`). A stateful function that returns the next
*counter block*, which is a byte string of `block_size` bytes.
For better performance, use `Crypto.Util.Counter`.
+ mac_len : integer
+ (*Only* `MODE_EAX`). Length of the MAC, in bytes.
+ It must be no larger than 8 (which is the default).
segment_size : integer
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in.
@@ -111,6 +121,8 @@ MODE_OFB = 5
MODE_CTR = 6
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
MODE_OPENPGP = 7
+#: EAX Mode. See `blockalgo.MODE_EAX`.
+MODE_EAX = 9
#: Size of a data block (in bytes)
block_size = 8
#: Size of a key (in bytes)
diff --git a/lib/Crypto/Cipher/DES3.py b/lib/Crypto/Cipher/DES3.py
index 7d20e5f..eae2dcc 100644
--- a/lib/Crypto/Cipher/DES3.py
+++ b/lib/Crypto/Cipher/DES3.py
@@ -87,6 +87,8 @@ def new(key, *args, **kwargs):
The chaining mode to use for encryption or decryption.
Default is `MODE_ECB`.
IV : byte string
+ (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
+
The initialization vector to use for encryption or decryption.
It is ignored for `MODE_ECB` and `MODE_CTR`.
@@ -95,12 +97,20 @@ def new(key, *args, **kwargs):
and `block_size` +2 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
It is mandatory.
-
- For all other modes, it must be `block_size` bytes longs.
+
+ For all other modes, it must be 8 bytes long.
+ nonce : byte string
+ (*Only* `MODE_EAX`).
+ A mandatory value that must never be reused for any other encryption.
+ There are no restrictions on its length, but it is recommended to
+ use at least 16 bytes.
counter : callable
(*Only* `MODE_CTR`). A stateful function that returns the next
- *counter block*, which is a byte string of `block_size` bytes.
+ *counter block*, which is a byte string of 8 bytes.
For better performance, use `Crypto.Util.Counter`.
+ mac_len : integer
+ (*Only* `MODE_EAX`). Length of the MAC, in bytes.
+ It must be no larger than 8 (which is the default).
segment_size : integer
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in.
@@ -126,6 +136,8 @@ MODE_OFB = 5
MODE_CTR = 6
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
MODE_OPENPGP = 7
+#: EAX Mode. See `blockalgo.MODE_EAX`.
+MODE_EAX = 9
#: Size of a data block (in bytes)
block_size = 8
#: Size of a key (in bytes)
diff --git a/lib/Crypto/Cipher/blockalgo.py b/lib/Crypto/Cipher/blockalgo.py
index 57738c4..be00832 100644
--- a/lib/Crypto/Cipher/blockalgo.py
+++ b/lib/Crypto/Cipher/blockalgo.py
@@ -31,7 +31,9 @@ from binascii import unhexlify
from Crypto.Util import Counter
from Crypto.Util.strxor import strxor
-from Crypto.Util.number import long_to_bytes
+from Crypto.Util.number import long_to_bytes, bytes_to_long
+import Crypto.Util.Counter
+from Crypto.Hash import CMAC
#: *Electronic Code Book (ECB)*.
#: This is the simplest encryption mode. Each of the plaintext blocks
@@ -184,6 +186,31 @@ MODE_OPENPGP = 7
#: .. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html
MODE_CCM = 8
+#: *EAX*. This is an Authenticated Encryption with Associated Data
+#: (`AEAD`_) mode. It provides both confidentiality and authenticity.
+#:
+#: The header of the message may be left in the clear, if needed, and it will
+#: still be subject to authentication.
+#:
+#: The decryption step tells the receiver if the message comes from a source
+#: that really knowns the secret key.
+#: Additionally, decryption detects if any part of the message - including the
+#: header - has been modified or corrupted.
+#:
+#: This mode requires a nonce. The nonce shall never repeat for two
+#: different messages encrypted with the same key, but it does not need to
+#: be random.
+#
+#: This mode is only available for ciphers that operate on 64 or
+#: 128 bits blocks.
+#:
+#: There are no official standards defining EAX. The implementation is based on
+#: `a proposal`__ that was presented to NIST.
+#:
+#: .. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html
+#: .. __: http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/eax/eax-spec.pdf
+MODE_EAX = 9
+
def _getParameter(name, index, args, kwargs, default=None):
"""Find a parameter in tuple and dictionary arguments a function receives"""
@@ -236,10 +263,44 @@ class BlockAlgo:
elif self.mode == MODE_OPENPGP:
self._start_PGP(factory, key, *args, **kwargs)
+ elif self.mode == MODE_EAX:
+ self._start_eax(factory, key, *args, **kwargs)
else:
self._cipher = factory.new(key, *args, **kwargs)
self.IV = self._cipher.IV
+ def _start_eax(self, factory, key, *args, **kwargs):
+
+ self.nonce = _getParameter('nonce', 1, args, kwargs)
+ if not self.nonce:
+ raise ValueError("MODE_EAX requires a nonce")
+
+ # Allowed transitions after initialization
+ self._next = [self.update, self.encrypt, self.decrypt,
+ self.digest, self.verify]
+
+ self._mac_len = kwargs.get('mac_len', self.block_size)
+ if not (self._mac_len and 4 <= self._mac_len <= self.block_size):
+ raise ValueError("Parameter 'mac_len' must not be larger than %d"
+ % self.block_size)
+
+ self._omac = [
+ CMAC.new(key, bchr(0) * (self.block_size - 1) + bchr(i),
+ ciphermod=factory)
+ for i in xrange(0, 3)
+ ]
+
+ # Compute MAC of nonce
+ self._omac[0].update(self.nonce)
+
+ # MAC of the nonce is also the initial counter for CTR encryption
+ counter_int = bytes_to_long(self._omac[0].digest())
+ counter_obj = Crypto.Util.Counter.new(
+ self.block_size * 8,
+ initial_value=counter_int,
+ allow_wraparound=True)
+ self._cipher = factory.new(key, MODE_CTR, counter=counter_obj)
+
def _start_PGP(self, factory, key, *args, **kwargs):
# OPENPGP mode. For details, see 13.9 in RCC4880.
#
@@ -348,7 +409,7 @@ class BlockAlgo:
def update(self, assoc_data):
"""Protect associated data
- When using an AEAD mode like CCM, and if there is any associated data,
+ When using an AEAD mode like CCM or EAX, and if there is any associated data,
the caller has to invoke this function one or more times, before
using ``decrypt`` or ``encrypt``.
@@ -356,7 +417,7 @@ class BlockAlgo:
will not be encrypted and will be transmitted in the clear.
However, the receiver is still able to detect any modification to it.
In CCM, the *associated data* is also called *additional authenticated
- data*.
+ data*. In EAX, the *associated data* is called *header*.
If there is no associated data, this method must not be called.
@@ -368,11 +429,11 @@ class BlockAlgo:
A piece of associated data. There are no restrictions on its size.
"""
- if self.mode == MODE_CCM:
+ if self.mode in (MODE_CCM, MODE_EAX):
if self.update not in self._next:
raise TypeError("update() can only be called immediately after initialization")
- self._next = [ self.update, self.encrypt, self.decrypt,
- self.digest, self.verify ]
+ self._next = [self.update, self.encrypt, self.decrypt,
+ self.digest, self.verify]
return self._update(assoc_data)
def _update(self, assoc_data, do_zero_padding=False):
@@ -398,6 +459,11 @@ class BlockAlgo:
self._assoc_buffer = [buf[aligned_data:]]
self._assoc_buffer_len -= aligned_data
return
+
+ if self.mode == MODE_EAX:
+ self._omac[1].update(assoc_data)
+ return
+
raise ValueError("update() not supported by this mode of operation")
def encrypt(self, plaintext):
@@ -425,8 +491,8 @@ class BlockAlgo:
- For `MODE_CFB`, *plaintext* length (in bytes) must be a multiple
of *segment_size*/8.
- - For `MODE_OFB`, `MODE_CTR` and `MODE_CCM` *plaintext* can be
- of any length.
+ - For `MODE_OFB`, `MODE_CTR`, `MODE_CCM` and `MODE_EAX`
+ *plaintext* can be of any length.
- For `MODE_OPENPGP`, *plaintext* must be a multiple of *block_size*,
unless it is the last chunk of the message.
@@ -460,12 +526,12 @@ class BlockAlgo:
self._done_first_block = True
return res
- if self.mode == MODE_CCM:
-
+ if self.mode in (MODE_CCM, MODE_EAX):
if self.encrypt not in self._next:
raise TypeError("encrypt() can only be called after initialization or an update()")
self._next = [self.encrypt, self.digest]
+ if self.mode == MODE_CCM:
if self._assoc_len is None:
self._start_ccm(assoc_len=self._assoc_buffer_len)
if self._msg_len is None:
@@ -477,7 +543,12 @@ class BlockAlgo:
self._update(plaintext)
- return self._cipher.encrypt(plaintext)
+ ct = self._cipher.encrypt(plaintext)
+
+ if self.mode == MODE_EAX:
+ self._omac[2].update(ct)
+
+ return ct
def decrypt(self, ciphertext):
"""Decrypt data with the key and the parameters set at initialization.
@@ -504,7 +575,7 @@ class BlockAlgo:
- For `MODE_CFB`, *ciphertext* length (in bytes) must be a multiple
of *segment_size*/8.
- - For `MODE_OFB`, `MODE_CTR` and `MODE_CCM`, *ciphertext* can be
+ - For `MODE_OFB`, `MODE_CTR`, `MODE_CCM`, and `MODE_EAX`, *ciphertext* can be
of any length.
- For `MODE_OPENPGP`, *plaintext* must be a multiple of *block_size*,
@@ -532,20 +603,24 @@ class BlockAlgo:
res = self._cipher.decrypt(ciphertext)
return res
- if self.mode == MODE_CCM:
+ if self.mode in (MODE_CCM, MODE_EAX):
if self.decrypt not in self._next:
raise TypeError("decrypt() can only be called after initialization or an update()")
self._next = [self.decrypt, self.verify]
- if self._assoc_len is None:
- self._start_ccm(assoc_len=self._assoc_buffer_len)
- if self._msg_len is None:
- self._start_ccm(msg_len=len(ciphertext))
- self._next = [self.verify]
- if not self._done_assoc_data:
- self._update(b(""), do_zero_padding=True)
- self._done_assoc_data = True
+ if self.mode == MODE_CCM:
+ if self._assoc_len is None:
+ self._start_ccm(assoc_len=self._assoc_buffer_len)
+ if self._msg_len is None:
+ self._start_ccm(msg_len=len(ciphertext))
+ self._next = [self.verify]
+ if not self._done_assoc_data:
+ self._update(b(""), do_zero_padding=True)
+ self._done_assoc_data = True
+
+ if self.mode == MODE_EAX:
+ self._omac[2].update(ciphertext)
pt = self._cipher.decrypt(ciphertext)
@@ -557,8 +632,8 @@ class BlockAlgo:
def digest(self):
"""Compute the *binary* MAC tag in an AEAD mode.
- When using an AEAD mode like CCM, the caller invokes this function
- at the very end.
+ When using an AEAD mode like CCM or EAX, the caller invokes
+ this function at the very end.
This method returns the MAC that shall be sent to the receiver,
together with the ciphertext.
@@ -566,19 +641,28 @@ class BlockAlgo:
:Return: the MAC, as a byte string.
"""
- if self.mode == MODE_CCM:
+ if self.mode in (MODE_CCM, MODE_EAX):
if self.digest not in self._next:
raise TypeError("digest() cannot be called when decrypting or validating a message")
self._next = [self.digest]
- if self._assoc_len is None:
- self._start_ccm(assoc_len=self._assoc_buffer_len)
- if self._msg_len is None:
- self._start_ccm(msg_len=0)
- self._update(b(""), do_zero_padding=True)
+ if self.mode == MODE_CCM:
+
+ if self._assoc_len is None:
+ self._start_ccm(assoc_len=self._assoc_buffer_len)
+ if self._msg_len is None:
+ self._start_ccm(msg_len=0)
+ self._update(b(""), do_zero_padding=True)
+ tag = strxor(self._t, self._s_0)[:self._mac_len]
+
+ if self.mode == MODE_EAX:
- return strxor(self._t, self._s_0)[:self._mac_len]
+ tag = bchr(0) * self.block_size
+ for i in xrange(3):
+ tag = strxor(tag, self._omac[i].digest())
+
+ return tag
raise TypeError("digest() not supported by this mode of operation")
@@ -594,8 +678,8 @@ class BlockAlgo:
def verify(self, mac_tag):
"""Validate the *binary* MAC tag in an AEAD mode.
- When using an AEAD mode like CCM, the caller invokes this function
- at the very end.
+ When using an AEAD mode like CCM or EAX, the caller invokes
+ this function at the very end.
This method checks if the decrypted message is indeed valid
(that is, if the key is correct) and it has not been
@@ -609,17 +693,26 @@ class BlockAlgo:
or the key is incorrect.
"""
- if self.mode == MODE_CCM:
+ if self.mode in (MODE_CCM, MODE_EAX):
if self.verify not in self._next:
raise TypeError("verify() cannot be called when encrypting a message")
self._next = [self.verify]
- if self._assoc_len is None:
- self._start_ccm(assoc_len=self._assoc_buffer_len)
- if self._msg_len is None:
- self._start_ccm(msg_len=0)
- self._update(b(""), do_zero_padding=True)
- u = strxor(self._t, self._s_0)[:self._mac_len]
+ if self.mode == MODE_CCM:
+
+ if self._assoc_len is None:
+ self._start_ccm(assoc_len=self._assoc_buffer_len)
+ if self._msg_len is None:
+ self._start_ccm(msg_len=0)
+ self._update(b(""), do_zero_padding=True)
+ u = strxor(self._t, self._s_0)[:self._mac_len]
+
+ if self.mode == MODE_EAX:
+
+ u = bchr(0)*self.block_size
+ for i in xrange(3):
+ u = strxor(u, self._omac[i].digest())
+ u = u[:self._mac_len]
res = 0
# Constant-time comparison
diff --git a/lib/Crypto/SelfTest/Cipher/common.py b/lib/Crypto/SelfTest/Cipher/common.py
index e52a781..603ab54 100644
--- a/lib/Crypto/SelfTest/Cipher/common.py
+++ b/lib/Crypto/SelfTest/Cipher/common.py
@@ -382,12 +382,12 @@ class CCMSplitEncryptionTest(unittest.TestCase):
class AEADTests(unittest.TestCase):
"""Tests generic to all AEAD modes"""
- def __init__(self, module, mode_name):
+ def __init__(self, module, mode_name, key_size):
unittest.TestCase.__init__(self)
self.module = module
self.mode_name = mode_name
self.mode = getattr(module, mode_name)
- self.key = b('\xFF')*16
+ self.key = b('\xFF')*key_size
self.iv = b('\x00')*10
self.description = "AEAD Test"
@@ -658,7 +658,7 @@ def make_block_tests(module, module_name, test_data, additional_params=dict()):
extra_tests_added = 1
# Extract associated data and MAC for AEAD modes
- if p_mode == 'CCM':
+ if p_mode in ('CCM', 'EAX'):
assoc_data, params['plaintext'] = params['plaintext'].split('|')
assoc_data2, params['ciphertext'], params['mac'] = params['ciphertext'].split('|')
params['assoc_data'] = assoc_data
@@ -687,10 +687,16 @@ def make_block_tests(module, module_name, test_data, additional_params=dict()):
CCMMACLengthTest(module),
CCMSplitEncryptionTest(module),
]
- for aead_mode in ("MODE_CCM",):
+ for aead_mode in ("MODE_CCM","MODE_EAX"):
if hasattr(module, aead_mode):
- tests += [
- AEADTests(module, aead_mode),
+ key_sizes = []
+ try:
+ key_sizes += module.key_size
+ except TypeError:
+ key_sizes = [ module.key_size ]
+ for ks in key_sizes:
+ tests += [
+ AEADTests(module, aead_mode, ks),
]
return tests
diff --git a/lib/Crypto/SelfTest/Cipher/test_AES.py b/lib/Crypto/SelfTest/Cipher/test_AES.py
index 878f56b..53d60c0 100644
--- a/lib/Crypto/SelfTest/Cipher/test_AES.py
+++ b/lib/Crypto/SelfTest/Cipher/test_AES.py
@@ -1679,6 +1679,86 @@ test_data = [
'RFC3610 Packet Vector #24',
dict(mode='CCM', nonce='008d493b30ae8b3c9696766cfa')
),
+
+ # Test vectors for EAX taken from http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf
+ # 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.
+ #
+ ( '6bfb914fd07eae6b|',
+ '6bfb914fd07eae6b||e037830e8389f27b025a2d6527e79d01',
+ '233952dee4d5ed5f9b9c6d6ff80ff478',
+ 'EAX spec Appendix G',
+ dict(mode='EAX', nonce='62EC67F9C3A4A407FCB2A8C49031A8B3')
+ ),
+
+ ( 'fa3bfd4806eb53fa|f7fb',
+ 'fa3bfd4806eb53fa|19dd|5c4c9331049d0bdab0277408f67967e5',
+ '91945d3f4dcbee0bf45ef52255f095a4',
+ 'EAX spec Appendix G',
+ dict(mode='EAX', nonce='BECAF043B0A23D843194BA972C66DEBD')
+ ),
+
+ ( '234a3463c1264ac6|1a47cb4933',
+ '234a3463c1264ac6|d851d5bae0|3a59f238a23e39199dc9266626c40f80',
+ '01f74ad64077f2e704c0f60ada3dd523',
+ 'EAX spec Appendix G',
+ dict(mode='EAX', nonce='70C3DB4F0D26368400A10ED05D2BFF5E')
+ ),
+
+ ( '33cce2eabff5a79d|481c9e39b1',
+ '33cce2eabff5a79d|632a9d131a|d4c168a4225d8e1ff755939974a7bede',
+ 'd07cf6cbb7f313bdde66b727afd3c5e8',
+ 'EAX spec Appendix G',
+ dict(mode='EAX', nonce='8408DFFF3C1A2B1292DC199E46B7D617')
+ ),
+
+ ( 'aeb96eaebe2970e9|40d0c07da5e4',
+ 'aeb96eaebe2970e9|071dfe16c675|cb0677e536f73afe6a14b74ee49844dd',
+ '35b6d0580005bbc12b0587124557d2c2',
+ 'EAX spec Appendix G',
+ dict(mode='EAX', nonce='FDB6B06676EEDC5C61D74276E1F8E816')
+ ),
+
+ ( 'd4482d1ca78dce0f|4de3b35c3fc039245bd1fb7d',
+ 'd4482d1ca78dce0f|835bb4f15d743e350e728414|abb8644fd6ccb86947c5e10590210a4f',
+ 'bd8e6e11475e60b268784c38c62feb22',
+ 'EAX spec Appendix G',
+ dict(mode='EAX', nonce='6EAC5C93072D8E8513F750935E46DA1B')
+ ),
+
+ ( '65d2017990d62528|8b0a79306c9ce7ed99dae4f87f8dd61636',
+ '65d2017990d62528|02083e3979da014812f59f11d52630da30|137327d10649b0aa6e1c181db617d7f2',
+ '7c77d6e813bed5ac98baa417477a2e7d',
+ 'EAX spec Appendix G',
+ dict(mode='EAX', nonce='1A8C98DCD73D38393B2BF1569DEEFC19')
+ ),
+
+ ( '54b9f04e6a09189a|1bda122bce8a8dbaf1877d962b8592dd2d56',
+ '54b9f04e6a09189a|2ec47b2c4954a489afc7ba4897edcdae8cc3|3b60450599bd02c96382902aef7f832a',
+ '5fff20cafab119ca2fc73549e20f5b0d',
+ 'EAX spec Appendix G',
+ dict(mode='EAX', nonce='DDE59B97D722156D4D9AFF2BC7559826')
+ ),
+
+ ( '899a175897561d7e|6cf36720872b8513f6eab1a8a44438d5ef11',
+ '899a175897561d7e|0de18fd0fdd91e7af19f1d8ee8733938b1e8|e7f6d2231618102fdb7fe55ff1991700',
+ 'a4a4782bcffd3ec5e7ef6d8c34a56123',
+ 'EAX spec Appendix G',
+ dict(mode='EAX', nonce='B781FCF2F75FA5A8DE97A9CA48E522EC')
+ ),
+
+ ( '126735fcc320d25a|ca40d7446e545ffaed3bd12a740a659ffbbb3ceab7',
+ '126735fcc320d25a|cb8920f87a6c75cff39627b56e3ed197c552d295a7|cfc46afc253b4652b1af3795b124ab6e',
+ '8395fcf1e95bebd697bd010bc766aac3',
+ 'EAX spec Appendix G',
+ dict(mode='EAX', nonce='22E7ADD93CFC6393C57EC0B3C17D6B44')
+ ),
]
def get_tests(config={}):
diff --git a/pct-speedtest.py b/pct-speedtest.py
index 378604f..56b352b 100644
--- a/pct-speedtest.py
+++ b/pct-speedtest.py
@@ -363,6 +363,8 @@ class Benchmark:
self.test_encryption("%s-CTR-LE" % (cipher_name,), module, key_bytes, "CTR-LE")
if hasattr(module, "MODE_CCM"):
self.test_encryption("%s-CCM" % (cipher_name,), module, key_bytes, module.MODE_CCM)
+ if hasattr(module, "MODE_EAX"):
+ self.test_encryption("%s-EAX" % (cipher_name,), module, key_bytes, module.MODE_EAX)
# Crypto.Cipher (stream ciphers)
for cipher_name, module, key_bytes in stream_specs: