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
or ciphertext pieces to have length multiple of the CFB segment size.
* 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
message match the actual ones.
* CTR cipher accepts parameter `nonce` and possibly `initial_value` in
alternative to `counter`.
Resolved issues
---------------

View file

@ -172,7 +172,8 @@ def new(key, mode, *args, **kwargs):
For all other modes, it must be 16 bytes long.
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.
@ -184,10 +185,10 @@ def new(key, mode, *args, **kwargs):
For `MODE_OCB`, its length must be in the range ``[1..15]``.
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,
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
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
are segmented in.
@ -204,6 +205,9 @@ def new(key, mode, *args, **kwargs):
assoc_len : integer
(*Only* `MODE_CCM`). Length of the associated data.
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 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.
It is ignored for `MODE_ECB` and `MODE_CTR`.
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
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.
nonce : byte string
(*Only* `MODE_EAX`).
(*Only* `MODE_EAX` and `MODE_CTR`).
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.
counter : object
(*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
are segmented in.
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
Maximum cryptographic strength of the key, in bits.
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.
It is ignored for `MODE_ECB` and `MODE_CTR`.
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
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.
nonce : byte string
(*Only* `MODE_EAX`).
(*Only* `MODE_EAX` and `MODE_CTR`).
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.
counter : object
(*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
are segmented in.
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.
"""

View file

@ -129,9 +129,12 @@ def new(key, mode, *args, **kwargs):
For all other modes, it must be 8 bytes long.
nonce : byte string
(*Only* `MODE_EAX`).
(*Only* `MODE_EAX` and `MODE_CTR`).
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.
counter : object
(*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
are segmented in.
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.
"""

View file

@ -123,9 +123,12 @@ def new(key, mode, *args, **kwargs):
For all other modes, it must be 8 bytes long.
nonce : byte string
(*Only* `MODE_EAX`).
(*Only* `MODE_EAX` and `MODE_CTR`).
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.
counter : object
(*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
are segmented in.
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.
"""

View file

@ -45,13 +45,11 @@ as `AES`.
As an example, encryption can be done as follows:
>>> from Crypto.Cipher import DES3
>>> from Crypto import Random
>>> from Crypto.Util import Counter
>>> from Crypto.Random import get_random_bytes
>>>
>>> key = b'Sixteen byte key'
>>> nonce = Random.new().read(DES3.block_size/2)
>>> ctr = Counter.new(DES3.block_size*8/2, prefix=nonce)
>>> cipher = DES3.new(key, DES3.MODE_CTR, counter=ctr)
>>> nonce = get_random_bytes(DES3.block_size/2)
>>> cipher = DES3.new(key, DES3.MODE_CTR, nonce=nonce)
>>> plaintext = b'We are no longer the knights who say ni!'
>>> 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.
nonce : byte string
(*Only* `MODE_EAX`).
(*Only* `MODE_EAX` and `MODE_CTR`).
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.
counter : object
(*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
are segmented in.
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,
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 import Counter
from Crypto.Util.strxor import strxor
from Crypto.Util.number import long_to_bytes
@ -163,11 +162,9 @@ class CcmMode(object):
# Start CTR cipher, by formatting the counter (A.3)
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._factory.MODE_CTR,
counter=ctr,
nonce=bchr(q - 1) + nonce,
**cipher_params)
# 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,
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", """
int CTR_start_operation(void *cipher,
@ -111,6 +113,10 @@ class CtrMode(object):
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)
self._state = VoidPointer()
result = raw_ctr_lib.CTR_start_operation(block_cipher.get(),
@ -179,7 +185,8 @@ class CtrMode(object):
c_size_t(len(plaintext)))
if result:
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)
return get_raw_buffer(ciphertext)
@ -223,7 +230,8 @@ class CtrMode(object):
c_size_t(len(ciphertext)))
if result:
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)
return get_raw_buffer(plaintext)
@ -236,14 +244,29 @@ def _create_ctr_cipher(factory, **kwargs):
The underlying block cipher, a module from ``Crypto.Cipher``.
:Keywords:
iv : byte string
The IV to use for CBC.
nonce : binary string
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
Alias for ``iv``.
The nonce must be shorter than the block size (it can have
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
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.
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)
try:
counter = kwargs.pop("counter")
except KeyError:
# Required by unit test
raise TypeError("Missing 'counter' parameter for CTR mode")
counter = kwargs.pop("counter", None)
nonce = kwargs.pop("nonce", None)
initial_value = kwargs.pop("initial_value", None)
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
# just a dictionary for backward compatibility.
@ -267,7 +322,8 @@ def _create_ctr_cipher(factory, **kwargs):
initial_value = _counter.pop("initial_value")
little_endian = _counter.pop("little_endian")
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
words = []
@ -281,10 +337,8 @@ def _create_ctr_cipher(factory, **kwargs):
if len(initial_counter_block) != factory.block_size:
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,
len(prefix), counter_len, little_endian)

View file

@ -34,9 +34,8 @@ EAX mode.
__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.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
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,
factory.MODE_CTR,
counter=counter_obj,
initial_value=counter_int,
nonce=b(""),
**cipher_params)
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 import Counter
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Hash import BLAKE2s
from Crypto.Random import get_random_bytes
@ -189,20 +188,20 @@ class GcmMode(object):
.digest())
# Step 3 - Prepare GCTR cipher for encryption/decryption
ctr = Counter.new(128, initial_value=self._j0 + 1)
self._cipher = factory.new(key,
self._factory.MODE_CTR,
counter=ctr,
initial_value=self._j0 + 1,
nonce=b(""),
**cipher_params)
# Step 5 - Bootstrat GHASH
self._signer = _GHASH(hash_subkey)
# Step 6 - Prepare GCTR cipher for GMAC
ctr = Counter.new(128, initial_value=self._j0)
self._tag_cipher = factory.new(key,
self._factory.MODE_CTR,
counter=ctr,
initial_value=self._j0,
nonce=b(""),
**cipher_params)
# Cache for data to authenticate

View file

@ -36,9 +36,8 @@ __all__ = ['SivMode']
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.Protocol.KDF import _S2V
from Crypto.Hash import BLAKE2s
@ -125,14 +124,11 @@ class SivMode(object):
"""Create a new CTR cipher from the MAC in SIV mode"""
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(
self._subkey_cipher,
self._factory.MODE_CTR,
counter=ctr,
initial_value=tag_int ^ (tag_int & 0x8000000080000000L),
nonce=b(""),
**self._cipher_params)
def update(self, component):

View file

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