Add nonce and initial_value parameters to CTR mode

This commit is contained in:
Legrandin 2015-12-20 21:27:21 +01:00
parent 6fc0c734a2
commit c74b718aa6
13 changed files with 210 additions and 71 deletions

View file

@ -13,9 +13,11 @@ New features
* For CFB/OPENPGP, `encrypt` and `decrypt` do not require the plaintext * For CFB/OPENPGP, `encrypt` and `decrypt` do not require the plaintext
or ciphertext pieces to have length multiple of the CFB segment size. or ciphertext pieces to have length multiple of the CFB segment size.
* Dedicated tests for all cipher modes, including NIST test vectors * Dedicated tests for all cipher modes, including NIST test vectors
* CCM/EAX/GCM/SIV cipher objects expose attribute `nonce`. * CTR/CCM/EAX/GCM/SIV cipher objects expose attribute `nonce`.
* CCM cipher checks if the declared lengths of the associated data and of the * CCM cipher checks if the declared lengths of the associated data and of the
message match the actual ones. message match the actual ones.
* CTR cipher accepts parameter `nonce` and possibly `initial_value` in
alternative to `counter`.
Resolved issues Resolved issues
--------------- ---------------

View file

@ -172,7 +172,8 @@ def new(key, mode, *args, **kwargs):
For all other modes, it must be 16 bytes long. For all other modes, it must be 16 bytes long.
nonce : byte string nonce : byte string
(*Only* `MODE_CCM`, `MODE_EAX`, `MODE_GCM`, `MODE_SIV`, `MODE_OCB`). (*Only* `MODE_CCM`, `MODE_EAX`, `MODE_GCM`, `MODE_SIV`, `MODE_OCB`,
`MODE_CTR`).
A mandatory value that must never be reused for any other encryption. A mandatory value that must never be reused for any other encryption.
@ -184,10 +185,10 @@ def new(key, mode, *args, **kwargs):
For `MODE_OCB`, its length must be in the range ``[1..15]``. For `MODE_OCB`, its length must be in the range ``[1..15]``.
It is recommended to use 15 bytes. It is recommended to use 15 bytes.
For `MODE_CTR`, its length must be in the range ``[0..15]``.
For the other modes, there are no restrictions on its length, For the other modes, there are no restrictions on its length,
but it is recommended to use at least 16 bytes. but it is recommended to use at least 16 bytes.
counter : object
(*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`.
segment_size : integer segment_size : integer
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in. are segmented in.
@ -204,6 +205,9 @@ def new(key, mode, *args, **kwargs):
assoc_len : integer assoc_len : integer
(*Only* `MODE_CCM`). Length of the associated data. (*Only* `MODE_CCM`). Length of the associated data.
If not specified, all data is internally buffered. If not specified, all data is internally buffered.
initial_value : integer
(*Only* `MODE_CTR`). The initial value for the counter within
the counter block. By default it is 0.
use_aesni : boolean use_aesni : boolean
Use AES-NI if available. Use AES-NI if available.

View file

@ -132,8 +132,6 @@ def new(key, mode, *args, **kwargs):
The initialization vector to use for encryption or decryption. The initialization vector to use for encryption or decryption.
It is ignored for `MODE_ECB` and `MODE_CTR`.
For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption
and `block_size` +2 bytes for decryption (in the latter case, it is and `block_size` +2 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext). actually the *encrypted* IV which was prefixed to the ciphertext).
@ -141,9 +139,12 @@ def new(key, mode, *args, **kwargs):
For all other modes, it must be 8 bytes long. For all other modes, it must be 8 bytes long.
nonce : byte string nonce : byte string
(*Only* `MODE_EAX`). (*Only* `MODE_EAX` and `MODE_CTR`).
A mandatory value that must never be reused for any other encryption. A mandatory value that must never be reused for any other encryption.
There are no restrictions on its length, but it is recommended to
For `MODE_CTR`, its length must be in the range ``[0..7]``.
For `MODE_EAX`, there are no restrictions, but it is recommended to
use at least 16 bytes. use at least 16 bytes.
counter : object counter : object
(*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`. (*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`.
@ -154,6 +155,9 @@ def new(key, mode, *args, **kwargs):
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in. are segmented in.
It must be a multiple of 8. If not specified, it will be assumed to be 8. It must be a multiple of 8. If not specified, it will be assumed to be 8.
initial_value : integer
(*Only* `MODE_CTR`). The initial value for the counter within
the counter block. By default it is 0.
effective_keylen : integer effective_keylen : integer
Maximum cryptographic strength of the key, in bits. Maximum cryptographic strength of the key, in bits.
It can vary from 0 to 1024. The default value is 1024. It can vary from 0 to 1024. The default value is 1024.

View file

@ -116,8 +116,6 @@ def new(key, mode, *args, **kwargs):
The initialization vector to use for encryption or decryption. The initialization vector to use for encryption or decryption.
It is ignored for `MODE_ECB` and `MODE_CTR`.
For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption
and `block_size` +2 bytes for decryption (in the latter case, it is and `block_size` +2 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext). actually the *encrypted* IV which was prefixed to the ciphertext).
@ -125,9 +123,12 @@ def new(key, mode, *args, **kwargs):
For all other modes, it must be 8 bytes long. For all other modes, it must be 8 bytes long.
nonce : byte string nonce : byte string
(*Only* `MODE_EAX`). (*Only* `MODE_EAX` and `MODE_CTR`).
A mandatory value that must never be reused for any other encryption. A mandatory value that must never be reused for any other encryption.
There are no restrictions on its length, but it is recommended to
For `MODE_CTR`, its length must be in the range ``[0..7]``.
For `MODE_EAX`, there are no restrictions, but it is recommended to
use at least 16 bytes. use at least 16 bytes.
counter : object counter : object
(*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`. (*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`.
@ -138,6 +139,9 @@ def new(key, mode, *args, **kwargs):
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in. are segmented in.
It must be a multiple of 8. If not specified, it will be assumed to be 8. It must be a multiple of 8. If not specified, it will be assumed to be 8.
initial_value : integer
(*Only* `MODE_CTR`). The initial value for the counter within
the counter block. By default it is 0.
:Return: a Blowfish cipher object, of the applicable mode. :Return: a Blowfish cipher object, of the applicable mode.
""" """

View file

@ -129,9 +129,12 @@ def new(key, mode, *args, **kwargs):
For all other modes, it must be 8 bytes long. For all other modes, it must be 8 bytes long.
nonce : byte string nonce : byte string
(*Only* `MODE_EAX`). (*Only* `MODE_EAX` and `MODE_CTR`).
A mandatory value that must never be reused for any other encryption. A mandatory value that must never be reused for any other encryption.
There are no restrictions on its length, but it is recommended to
For `MODE_CTR`, its length must be in the range ``[0..7]``.
For `MODE_EAX`, there are no restrictions, but it is recommended to
use at least 16 bytes. use at least 16 bytes.
counter : object counter : object
(*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`. (*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`.
@ -142,6 +145,9 @@ def new(key, mode, *args, **kwargs):
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in. are segmented in.
It must be a multiple of 8. If not specified, it will be assumed to be 8. It must be a multiple of 8. If not specified, it will be assumed to be 8.
initial_value : integer
(*Only* `MODE_CTR`). The initial value for the counter within
the counter block. By default it is 0.
:Return: a CAST cipher object, of the applicable mode. :Return: a CAST cipher object, of the applicable mode.
""" """

View file

@ -123,9 +123,12 @@ def new(key, mode, *args, **kwargs):
For all other modes, it must be 8 bytes long. For all other modes, it must be 8 bytes long.
nonce : byte string nonce : byte string
(*Only* `MODE_EAX`). (*Only* `MODE_EAX` and `MODE_CTR`).
A mandatory value that must never be reused for any other encryption. A mandatory value that must never be reused for any other encryption.
There are no restrictions on its length, but it is recommended to
For `MODE_CTR`, its length must be in the range ``[0..7]``.
For `MODE_EAX`, there are no restrictions, but it is recommended to
use at least 16 bytes. use at least 16 bytes.
counter : object counter : object
(*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`. (*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`.
@ -136,6 +139,9 @@ def new(key, mode, *args, **kwargs):
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in. are segmented in.
It must be a multiple of 8. If not specified, it will be assumed to be 8. It must be a multiple of 8. If not specified, it will be assumed to be 8.
initial_value : integer
(*Only* `MODE_CTR`). The initial value for the counter within
the counter block. By default it is 0.
:Return: a DES cipher, of the applicable mode. :Return: a DES cipher, of the applicable mode.
""" """

View file

@ -45,13 +45,11 @@ as `AES`.
As an example, encryption can be done as follows: As an example, encryption can be done as follows:
>>> from Crypto.Cipher import DES3 >>> from Crypto.Cipher import DES3
>>> from Crypto import Random >>> from Crypto.Random import get_random_bytes
>>> from Crypto.Util import Counter
>>> >>>
>>> key = b'Sixteen byte key' >>> key = b'Sixteen byte key'
>>> nonce = Random.new().read(DES3.block_size/2) >>> nonce = get_random_bytes(DES3.block_size/2)
>>> ctr = Counter.new(DES3.block_size*8/2, prefix=nonce) >>> cipher = DES3.new(key, DES3.MODE_CTR, nonce=nonce)
>>> cipher = DES3.new(key, DES3.MODE_CTR, counter=ctr)
>>> plaintext = b'We are no longer the knights who say ni!' >>> plaintext = b'We are no longer the knights who say ni!'
>>> msg = nonce + cipher.encrypt(plaintext) >>> msg = nonce + cipher.encrypt(plaintext)
@ -138,9 +136,12 @@ def new(key, mode, *args, **kwargs):
For all other modes, it must be 8 bytes long. For all other modes, it must be 8 bytes long.
nonce : byte string nonce : byte string
(*Only* `MODE_EAX`). (*Only* `MODE_EAX` and `MODE_CTR`).
A mandatory value that must never be reused for any other encryption. A mandatory value that must never be reused for any other encryption.
There are no restrictions on its length, but it is recommended to
For `MODE_CTR`, its length must be in the range ``[0..7]``.
For `MODE_EAX`, there are no restrictions, but it is recommended to
use at least 16 bytes. use at least 16 bytes.
counter : object counter : object
(*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`. (*Only* `MODE_CTR`). An object created by `Crypto.Util.Counter`.
@ -151,6 +152,9 @@ def new(key, mode, *args, **kwargs):
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in. are segmented in.
It must be a multiple of 8. If not specified, it will be assumed to be 8. It must be a multiple of 8. If not specified, it will be assumed to be 8.
initial_value : integer
(*Only* `MODE_CTR`). The initial value for the counter within
the counter block. By default it is 0.
:Attention: it is important that all 8 byte subkeys are different, :Attention: it is important that all 8 byte subkeys are different,
otherwise TDES would degrade to single `DES`. otherwise TDES would degrade to single `DES`.

View file

@ -36,7 +36,6 @@ __all__ = ['CcmMode']
from Crypto.Util.py3compat import byte_string, b, bchr, bord, unhexlify from Crypto.Util.py3compat import byte_string, b, bchr, bord, unhexlify
from Crypto.Util import Counter
from Crypto.Util.strxor import strxor from Crypto.Util.strxor import strxor
from Crypto.Util.number import long_to_bytes from Crypto.Util.number import long_to_bytes
@ -163,11 +162,9 @@ class CcmMode(object):
# Start CTR cipher, by formatting the counter (A.3) # Start CTR cipher, by formatting the counter (A.3)
q = 15 - len(nonce) # length of Q, the encoded message length q = 15 - len(nonce) # length of Q, the encoded message length
prefix = bchr(q - 1) + nonce
ctr = Counter.new(128 - len(prefix) * 8, prefix, initial_value=0)
self._cipher = self._factory.new(key, self._cipher = self._factory.new(key,
self._factory.MODE_CTR, self._factory.MODE_CTR,
counter=ctr, nonce=bchr(q - 1) + nonce,
**cipher_params) **cipher_params)
# S_0, step 6 in 6.1 for j=0 # S_0, step 6 in 6.1 for j=0

View file

@ -30,7 +30,9 @@ from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer,
create_string_buffer, get_raw_buffer, create_string_buffer, get_raw_buffer,
SmartPointer, c_size_t, expect_byte_string) SmartPointer, c_size_t, expect_byte_string)
from Crypto.Util.py3compat import * from Crypto.Random import get_random_bytes
from Crypto.Util.py3compat import b, bchr
from Crypto.Util.number import long_to_bytes
raw_ctr_lib = load_pycryptodome_raw_lib("Crypto.Cipher._raw_ctr", """ raw_ctr_lib = load_pycryptodome_raw_lib("Crypto.Cipher._raw_ctr", """
int CTR_start_operation(void *cipher, int CTR_start_operation(void *cipher,
@ -111,6 +113,10 @@ class CtrMode(object):
in little endian mode. If False, it is big endian. in little endian mode. If False, it is big endian.
""" """
if len(initial_counter_block) == prefix_len + counter_len:
#: Nonce; not available if there is a fixed suffix
self.nonce = initial_counter_block[:prefix_len]
expect_byte_string(initial_counter_block) expect_byte_string(initial_counter_block)
self._state = VoidPointer() self._state = VoidPointer()
result = raw_ctr_lib.CTR_start_operation(block_cipher.get(), result = raw_ctr_lib.CTR_start_operation(block_cipher.get(),
@ -179,7 +185,8 @@ class CtrMode(object):
c_size_t(len(plaintext))) c_size_t(len(plaintext)))
if result: if result:
if result == 0x60002: if result == 0x60002:
raise OverflowError("The counter has wrapped around in CTR mode") raise OverflowError("The counter has wrapped around in"
" CTR mode")
raise ValueError("Error %X while encrypting in CTR mode" % result) raise ValueError("Error %X while encrypting in CTR mode" % result)
return get_raw_buffer(ciphertext) return get_raw_buffer(ciphertext)
@ -223,7 +230,8 @@ class CtrMode(object):
c_size_t(len(ciphertext))) c_size_t(len(ciphertext)))
if result: if result:
if result == 0x60002: if result == 0x60002:
raise OverflowError("The counter has wrapped around in CTR mode") raise OverflowError("The counter has wrapped around in"
" CTR mode")
raise ValueError("Error %X while decrypting in CTR mode" % result) raise ValueError("Error %X while decrypting in CTR mode" % result)
return get_raw_buffer(plaintext) return get_raw_buffer(plaintext)
@ -236,14 +244,29 @@ def _create_ctr_cipher(factory, **kwargs):
The underlying block cipher, a module from ``Crypto.Cipher``. The underlying block cipher, a module from ``Crypto.Cipher``.
:Keywords: :Keywords:
iv : byte string nonce : binary string
The IV to use for CBC. The fixed part at the beginning of the counter block - the rest is
the counter number that gets increased when processing the next block.
The nonce must be such that no two messages are encrypted under the
same key and the same nonce.
IV : byte string The nonce must be shorter than the block size (it can have
Alias for ``iv``. zero length).
If this parameter is not present, a random nonce will be created with
length equal to half the block size. No random nonce shorter than
64 bits will be created though - you must really think through all
security consequences of using such a short block size.
initial_value : posive integer
The initial value for the counter. If not present, the cipher will
start counting from 0. The value is incremented by one for each block.
The counter number is encoded in big endian mode.
counter : object counter : object
Instance of ``Crypto.Util.Counter``. Instance of ``Crypto.Util.Counter``, which allows full customization
of the counter block. This parameter is incompatible to both ``nonce``
and ``initial_value``.
Any other keyword will be passed to the underlying block cipher. Any other keyword will be passed to the underlying block cipher.
See the relevant documentation for details (at least ``key`` will need See the relevant documentation for details (at least ``key`` will need
@ -251,11 +274,43 @@ def _create_ctr_cipher(factory, **kwargs):
""" """
cipher_state = factory._create_base_cipher(kwargs) cipher_state = factory._create_base_cipher(kwargs)
try:
counter = kwargs.pop("counter") counter = kwargs.pop("counter", None)
except KeyError: nonce = kwargs.pop("nonce", None)
# Required by unit test initial_value = kwargs.pop("initial_value", None)
raise TypeError("Missing 'counter' parameter for CTR mode") if kwargs:
raise TypeError("Invalid parameters for CTR mode: %s" % str(kwargs))
if counter is not None and (nonce, initial_value) != (None, None):
raise TypeError("'counter' and 'nonce'/'initial_value'"
" are mutually exclusive")
if counter is None:
# Crypto.Util.Counter is not used
if nonce is None:
if factory.block_size < 16:
raise TypeError("Impossible to create a safe nonce for short"
" block sizes")
nonce = get_random_bytes(factory.block_size // 2)
if initial_value is None:
initial_value = 0
if len(nonce) >= factory.block_size:
raise ValueError("Nonce is too long")
counter_len = factory.block_size - len(nonce)
if (1 << (counter_len * 8)) - 1 < initial_value:
raise ValueError("Initial counter value is too large")
return CtrMode(cipher_state,
# initial_counter_block
nonce + long_to_bytes(initial_value, counter_len),
len(nonce), # prefix
counter_len,
False) # little_endian
# Crypto.Util.Counter is used
# 'counter' used to be a callable object, but now it is # 'counter' used to be a callable object, but now it is
# just a dictionary for backward compatibility. # just a dictionary for backward compatibility.
@ -267,7 +322,8 @@ def _create_ctr_cipher(factory, **kwargs):
initial_value = _counter.pop("initial_value") initial_value = _counter.pop("initial_value")
little_endian = _counter.pop("little_endian") little_endian = _counter.pop("little_endian")
except KeyError: except KeyError:
raise TypeError("Incorrect counter object (use Crypto.Util.Counter.new)") raise TypeError("Incorrect counter object"
" (use Crypto.Util.Counter.new)")
# Compute initial counter block # Compute initial counter block
words = [] words = []
@ -281,10 +337,8 @@ def _create_ctr_cipher(factory, **kwargs):
if len(initial_counter_block) != factory.block_size: if len(initial_counter_block) != factory.block_size:
raise ValueError("Size of the counter block (% bytes) must match" raise ValueError("Size of the counter block (% bytes) must match"
" block size (%d)", (len(initial_counter_block), factory.block_size)) " block size (%d)" % (len(initial_counter_block),
factory.block_size))
if kwargs:
raise TypeError("Unknown parameters for CTR mode: %s"
% str(kwargs))
return CtrMode(cipher_state, initial_counter_block, return CtrMode(cipher_state, initial_counter_block,
len(prefix), counter_len, little_endian) len(prefix), counter_len, little_endian)

View file

@ -34,9 +34,8 @@ EAX mode.
__all__ = ['EaxMode'] __all__ = ['EaxMode']
from Crypto.Util.py3compat import byte_string, bchr, bord, unhexlify from Crypto.Util.py3compat import byte_string, bchr, bord, unhexlify, b
from Crypto.Util import Counter
from Crypto.Util.strxor import strxor from Crypto.Util.strxor import strxor
from Crypto.Util.number import long_to_bytes, bytes_to_long from Crypto.Util.number import long_to_bytes, bytes_to_long
@ -109,12 +108,10 @@ class EaxMode(object):
# MAC of the nonce is also the initial counter for CTR encryption # MAC of the nonce is also the initial counter for CTR encryption
counter_int = bytes_to_long(self._omac[0].digest()) counter_int = bytes_to_long(self._omac[0].digest())
counter_obj = Counter.new(
self.block_size * 8,
initial_value=counter_int)
self._cipher = factory.new(key, self._cipher = factory.new(key,
factory.MODE_CTR, factory.MODE_CTR,
counter=counter_obj, initial_value=counter_int,
nonce=b(""),
**cipher_params) **cipher_params)
def update(self, assoc_data): def update(self, assoc_data):

View file

@ -36,7 +36,6 @@ __all__ = ['GcmMode']
from Crypto.Util.py3compat import b, bchr, byte_string, bord, unhexlify from Crypto.Util.py3compat import b, bchr, byte_string, bord, unhexlify
from Crypto.Util import Counter
from Crypto.Util.number import long_to_bytes, bytes_to_long from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Hash import BLAKE2s from Crypto.Hash import BLAKE2s
from Crypto.Random import get_random_bytes from Crypto.Random import get_random_bytes
@ -189,20 +188,20 @@ class GcmMode(object):
.digest()) .digest())
# Step 3 - Prepare GCTR cipher for encryption/decryption # Step 3 - Prepare GCTR cipher for encryption/decryption
ctr = Counter.new(128, initial_value=self._j0 + 1)
self._cipher = factory.new(key, self._cipher = factory.new(key,
self._factory.MODE_CTR, self._factory.MODE_CTR,
counter=ctr, initial_value=self._j0 + 1,
nonce=b(""),
**cipher_params) **cipher_params)
# Step 5 - Bootstrat GHASH # Step 5 - Bootstrat GHASH
self._signer = _GHASH(hash_subkey) self._signer = _GHASH(hash_subkey)
# Step 6 - Prepare GCTR cipher for GMAC # Step 6 - Prepare GCTR cipher for GMAC
ctr = Counter.new(128, initial_value=self._j0)
self._tag_cipher = factory.new(key, self._tag_cipher = factory.new(key,
self._factory.MODE_CTR, self._factory.MODE_CTR,
counter=ctr, initial_value=self._j0,
nonce=b(""),
**cipher_params) **cipher_params)
# Cache for data to authenticate # Cache for data to authenticate

View file

@ -36,9 +36,8 @@ __all__ = ['SivMode']
from binascii import hexlify from binascii import hexlify
from Crypto.Util.py3compat import byte_string, bord, unhexlify from Crypto.Util.py3compat import byte_string, bord, unhexlify, b
from Crypto.Util import Counter
from Crypto.Util.number import long_to_bytes, bytes_to_long from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Protocol.KDF import _S2V from Crypto.Protocol.KDF import _S2V
from Crypto.Hash import BLAKE2s from Crypto.Hash import BLAKE2s
@ -125,14 +124,11 @@ class SivMode(object):
"""Create a new CTR cipher from the MAC in SIV mode""" """Create a new CTR cipher from the MAC in SIV mode"""
tag_int = bytes_to_long(mac_tag) tag_int = bytes_to_long(mac_tag)
init_counter = tag_int ^ (tag_int & 0x8000000080000000L)
ctr = Counter.new(self.block_size * 8,
initial_value=init_counter)
return self._factory.new( return self._factory.new(
self._subkey_cipher, self._subkey_cipher,
self._factory.MODE_CTR, self._factory.MODE_CTR,
counter=ctr, initial_value=tag_int ^ (tag_int & 0x8000000080000000L),
nonce=b(""),
**self._cipher_params) **self._cipher_params)
def update(self, component): def update(self, component):

View file

@ -43,8 +43,10 @@ class CtrTests(unittest.TestCase):
key_128 = get_tag_random("key_128", 16) key_128 = get_tag_random("key_128", 16)
key_192 = get_tag_random("key_192", 24) key_192 = get_tag_random("key_192", 24)
ctr_64 = Counter.new(32, prefix=get_tag_random("iv_64", 4)) nonce_32 = get_tag_random("nonce_32", 4)
ctr_128 = Counter.new(64, prefix=get_tag_random("iv_128", 8)) 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): def test_loopback_128(self):
cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128)
@ -64,10 +66,74 @@ class CtrTests(unittest.TestCase):
pt2 = cipher.decrypt(ct) pt2 = cipher.decrypt(ct)
self.assertEqual(pt, pt2) self.assertEqual(pt, pt2)
def test_counter_is_required(self): def test_invalid_counter_parameter(self):
self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR) # 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) 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.failIf(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)
# 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_iv_with_matching_length(self): def test_iv_with_matching_length(self):
self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR, self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR,
counter=Counter.new(120)) counter=Counter.new(120))