Modern modes of operation for symmetric block ciphers

Classic modes of operation such as CBC only provide guarantees over the confidentiality of the message but not over its integrity. In other words, they don’t allow the receiver to establish if the ciphertext was modified in transit or if it really originates from a certain source.

For that reason, classic modes of operation have been often paired with a MAC primitive (such as Crypto.Hash.HMAC), but the combination is not always straightforward, efficient or secure.

Recently, new modes of operations (AEAD, for Authenticated Encryption with Associated Data) have been designed to combine encryption and authentication into a single, efficient primitive. Optionally, some part of the message can also be left in the clear (non-confidential associated data, such as headers), while the whole message remains fully authenticated.

In addition to the ciphertext and a nonce (or IV - Initialization Vector), AEAD modes require the additional delivery of a MAC tag.

This is the state machine for a cipher object:

../../_images/aead.png

Fig. 3 Generic state diagram for a AEAD cipher mode

Besides encrypt() and decrypt(), which are also available in classic modes of operation, a cipher object for an AEAD mode has these methods:

Method

Purpose

update(data)

Authenticate data that is delivered as it is, without encryption (associated data, like a header). Call it before encrypt() or decrypt(). Data passed to encrypt() and decrypt() is authenticated automatically.

digest(), hexdigest()

At the end of encryption, return the MAC tag to deliver with the ciphertext (as bytes or as a hexadecimal string).

verify(tag), hexverify(hex_tag)

At the end of decryption, check the MAC tag received with the ciphertext. They raise ValueError if the tag is not valid: then the key is wrong or the message was modified, and you must discard the plaintext.

encrypt_and_digest(plaintext)

encrypt() and digest() in one call: returns the tuple (ciphertext, tag).

decrypt_and_verify(ciphertext, tag)

decrypt() and verify() in one call: returns the plaintext, or raises ValueError if the tag is not valid.

The full API of each mode is at the end of its section below (for instance, GcmMode for GCM).

CCM mode

Counter with CBC-MAC, defined in RFC3610 or NIST SP 800-38C. It only works with ciphers having block size 128 bits (like AES).

The new() function at the module level under Crypto.Cipher instantiates a new CCM cipher object for the relevant base algorithm. In the following definition, <algorithm> can only be AES today:

Crypto.Cipher.<algorithm>.new(key, mode, *, nonce=None, mac_len=None, msg_len=None, assoc_len=None)

Create a new CCM object, using <algorithm> as the base block cipher.

Parameters:
  • key (bytes) – the cryptographic key

  • mode – the constant Crypto.Cipher.<algorithm>.MODE_CCM

  • nonce (bytes) – the value of the fixed nonce. It must be unique for the combination message/key. For AES, its length varies from 7 to 13 bytes. The longer the nonce, the smaller the allowed message size (with a nonce of 13 bytes, the message cannot exceed 64KB). If not present, the library creates a 11 bytes random nonce (the maximum message size is 4GB).

  • mac_len (integer) – the desired length of the MAC tag (default if not present: 16 bytes).

  • msg_len (integer) – pre-declaration of the length of the message to encipher. If not specified, encrypt() and decrypt() can only be called once.

  • assoc_len (integer) – pre-declaration of the length of the associated data. If not specified, some extra buffering will take place internally.

Returns:

a CCM cipher object (CcmMode)

The cipher object has a read-only attribute nonce.

Example (encryption):

>>> import json
>>> from base64 import b64encode
>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> header = b"header"
>>> data = b"secret"
>>> key = get_random_bytes(16)
>>> cipher = AES.new(key, AES.MODE_CCM)
>>> cipher.update(header)
>>> ciphertext, tag = cipher.encrypt_and_digest(data)
>>>
>>> json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
>>> json_v = [ b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag) ]
>>> result = json.dumps(dict(zip(json_k, json_v)))
>>> print(result)
{"nonce": "p6ffzcKw+6xopVQ=", "header": "aGVhZGVy", "ciphertext": "860kZo/G", "tag": "Ck5YpVCM6fdWnFkFxw8K6A=="}

Example (decryption):

>>> import json
>>> from base64 import b64decode
>>> from Crypto.Cipher import AES
>>>
>>> # We assume that the key was securely shared beforehand
>>> try:
...     b64 = json.loads(json_input)
...     json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
...     jv = {k:b64decode(b64[k]) for k in json_k}
...
...     cipher = AES.new(key, AES.MODE_CCM, nonce=jv['nonce'])
...     cipher.update(jv['header'])
...     plaintext = cipher.decrypt_and_verify(jv['ciphertext'], jv['tag'])
...     print("The message was: " + plaintext.decode('utf-8'))
... except (ValueError, KeyError):
...     print("Incorrect decryption")

CCM cipher object

class Crypto.Cipher._mode_ccm.CcmMode

Counter with CBC-MAC (CCM).

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 knows 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. Note that there is a trade-off between the size of the nonce and the maximum size of a single message you can encrypt.

It is important to use a large nonce if the key is reused across several messages and the nonce is chosen randomly.

It is acceptable to us a short nonce if the key is only used a few times or if the nonce is taken from a counter.

The following table shows the trade-off when the nonce is chosen at random. The column on the left shows how many messages it takes for the keystream to repeat on average. In practice, you will want to stop using the key way before that.

Avg. # of messages before keystream repeats

nonce size (bytes)

Max. message size (bytes)

2^52

13

64K

2^48

12

16M

2^44

11

4G

2^40

10

1T

2^36

9

64P

2^32

8

16E

This mode is only available for ciphers that operate on 128 bits blocks (e.g. AES but not TDES).

See NIST SP800-38C or RFC3610.

block_size

The block size of the underlying cipher, in bytes.

nonce

The nonce originally used to create the object.

update(assoc_data)

Protect associated data

If there is any associated data, the caller has to invoke this function one or more times, before using decrypt or encrypt.

By associated data it is meant any data (e.g. packet headers) that 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 (AAD).

If there is no associated data, this method must not be called.

The caller may split associated data in segments of any size, and invoke this method multiple times, each time with the next segment.

Parameters:

assoc_data (bytes/bytearray/memoryview) – A piece of associated data. There are no restrictions on its size.

Return type:

CcmMode

encrypt(plaintext, output=None)

Encrypt data with the key set at initialization.

A cipher object is stateful: once you have encrypted a message you cannot encrypt (or decrypt) another message using the same object.

This method can be called only once if msg_len was not passed at initialization.

If msg_len was given, the data to encrypt can be broken up in two or more pieces and encrypt can be called multiple times.

That is, the statement:

>>> c.encrypt(a) + c.encrypt(b)

is equivalent to:

>>> c.encrypt(a+b)

This function does not add any padding to the plaintext.

Parameters:

plaintext (bytes/bytearray/memoryview) – The piece of data to encrypt. It can be of any length.

Keyword Arguments:

output (bytearray/memoryview) – The location where the ciphertext must be written to. If None, the ciphertext is returned.

Return type:

Optional[bytes]

Returns:

If output is None, the ciphertext as bytes. Otherwise, None.

decrypt(ciphertext, output=None)

Decrypt data with the key set at initialization.

A cipher object is stateful: once you have decrypted a message you cannot decrypt (or encrypt) another message with the same object.

This method can be called only once if msg_len was not passed at initialization.

If msg_len was given, the data to decrypt can be broken up in two or more pieces and decrypt can be called multiple times.

That is, the statement:

>>> c.decrypt(a) + c.decrypt(b)

is equivalent to:

>>> c.decrypt(a+b)

This function does not remove any padding from the plaintext.

Parameters:

ciphertext (bytes/bytearray/memoryview) – The piece of data to decrypt. It can be of any length.

Keyword Arguments:

output (bytearray/memoryview) – The location where the plaintext must be written to. If None, the plaintext is returned.

Return type:

Optional[bytes]

Returns:

If output is None, the plaintext as bytes. Otherwise, None.

digest()

Compute the binary MAC tag.

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.

Return type:

bytes

Returns:

the MAC, as a byte string.

hexdigest()

Compute the printable MAC tag.

This method is like digest.

Return type:

str

Returns:

the MAC, as a hexadecimal string.

verify(received_mac_tag)

Validate the binary MAC tag.

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 tampered with while in transit.

Parameters:

received_mac_tag (bytes/bytearray/memoryview) – This is the binary MAC, as received from the sender.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

Return type:

None

hexverify(hex_mac_tag)

Validate the printable MAC tag.

This method is like verify.

Parameters:

hex_mac_tag (string) – This is the printable MAC, as received from the sender.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

Return type:

None

encrypt_and_digest(plaintext, output=None)

Perform encrypt() and digest() in one step.

Parameters:

plaintext (bytes/bytearray/memoryview) – The piece of data to encrypt.

Keyword Arguments:

output (bytearray/memoryview) – The location where the ciphertext must be written to. If None, the ciphertext is returned.

Return type:

tuple[Optional[bytes], bytes]

Returns:

The tuple (ciphertext, tag), both bytes. The ciphertext is None when the output parameter specified a location for the result.

decrypt_and_verify(ciphertext, received_mac_tag, output=None)

Perform decrypt() and verify() in one step.

Parameters:
  • ciphertext (bytes/bytearray/memoryview) – The piece of data to decrypt.

  • received_mac_tag (bytes/bytearray/memoryview) – This is the binary MAC, as received from the sender.

Keyword Arguments:

output (bytearray/memoryview) – The location where the plaintext must be written to. If None, the plaintext is returned.

Return type:

Optional[bytes]

Returns:

the plaintext as bytes or None when the output parameter specified a location for the result.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

EAX mode

An AEAD mode designed for NIST by Bellare, Rogaway, and Wagner in 2003.

The new() function at the module level under Crypto.Cipher instantiates a new EAX cipher object for the relevant base algorithm.

Crypto.Cipher.<algorithm>.new(key, mode, *, nonce=None, mac_len=None)

Create a new EAX object, using <algorithm> as the base block cipher.

Parameters:
  • key (bytes) – the cryptographic key

  • mode – the constant Crypto.Cipher.<algorithm>.MODE_EAX

  • nonce (bytes) – the value of the fixed nonce. It must be unique for the combination message/key. If not present, the library creates a random nonce (16 bytes long for AES).

  • mac_len (integer) – the length of the MAC tag, in bytes. At least 2, and not larger than the cipher’s block size (default), which is 16 bytes for AES.

Returns:

an EAX cipher object (EaxMode)

The cipher object has a read-only attribute nonce.

Example (encryption):

>>> import json
>>> from base64 import b64encode
>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> header = b"header"
>>> data = b"secret"
>>> key = get_random_bytes(16)
>>> cipher = AES.new(key, AES.MODE_EAX)
>>> cipher.update(header)
>>> ciphertext, tag = cipher.encrypt_and_digest(data)
>>>
>>> json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
>>> json_v = [ b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag) ]
>>> result = json.dumps(dict(zip(json_k, json_v)))
>>> print(result)
{"nonce": "CSIJ+e8KP7HJo+hC4RXIyQ==", "header": "aGVhZGVy", "ciphertext": "9YYjuAn6", "tag": "kXHrs9ZwYmjDkmfEJx7Clg=="}

Example (decryption):

>>> import json
>>> from base64 import b64decode
>>> from Crypto.Cipher import AES
>>>
>>> # We assume that the key was securely shared beforehand
>>> try:
...     b64 = json.loads(json_input)
...     json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
...     jv = {k:b64decode(b64[k]) for k in json_k}
...
...     cipher = AES.new(key, AES.MODE_EAX, nonce=jv['nonce'])
...     cipher.update(jv['header'])
...     plaintext = cipher.decrypt_and_verify(jv['ciphertext'], jv['tag'])
...     print("The message was: " + plaintext.decode('utf-8'))
... except (ValueError, KeyError):
...     print("Incorrect decryption")

EAX cipher object

class Crypto.Cipher._mode_eax.EaxMode

EAX mode.

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 knows 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.

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.

block_size

The block size of the underlying cipher, in bytes.

nonce

The nonce originally used to create the object.

update(assoc_data)

Protect associated data

If there is any associated data, the caller has to invoke this function one or more times, before using decrypt or encrypt.

By associated data it is meant any data (e.g. packet headers) that will not be encrypted and will be transmitted in the clear. However, the receiver is still able to detect any modification to it.

If there is no associated data, this method must not be called.

The caller may split associated data in segments of any size, and invoke this method multiple times, each time with the next segment.

Parameters:

assoc_data (bytes/bytearray/memoryview) – A piece of associated data. There are no restrictions on its size.

Return type:

EaxMode

encrypt(plaintext, output=None)

Encrypt data with the key and the parameters set at initialization.

A cipher object is stateful: once you have encrypted a message you cannot encrypt (or decrypt) another message using the same object.

The data to encrypt can be broken up in two or more pieces and encrypt can be called multiple times.

That is, the statement:

>>> c.encrypt(a) + c.encrypt(b)

is equivalent to:

>>> c.encrypt(a+b)

This function does not add any padding to the plaintext.

Parameters:

plaintext (bytes/bytearray/memoryview) – The piece of data to encrypt. It can be of any length.

Keyword Arguments:

output (bytearray/memoryview) – The location where the ciphertext must be written to. If None, the ciphertext is returned.

Return type:

Optional[bytes]

Returns:

If output is None, the ciphertext as bytes. Otherwise, None.

decrypt(ciphertext, output=None)

Decrypt data with the key and the parameters set at initialization.

A cipher object is stateful: once you have decrypted a message you cannot decrypt (or encrypt) another message with the same object.

The data to decrypt can be broken up in two or more pieces and decrypt can be called multiple times.

That is, the statement:

>>> c.decrypt(a) + c.decrypt(b)

is equivalent to:

>>> c.decrypt(a+b)

This function does not remove any padding from the plaintext.

Parameters:

ciphertext (bytes/bytearray/memoryview) – The piece of data to decrypt. It can be of any length.

Keyword Arguments:

output (bytearray/memoryview) – The location where the plaintext must be written to. If None, the plaintext is returned.

Return type:

Optional[bytes]

Returns:

If output is None, the plaintext as bytes. Otherwise, None.

digest()

Compute the binary MAC tag.

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.

Return type:

bytes

Returns:

the MAC, as a byte string.

hexdigest()

Compute the printable MAC tag.

This method is like digest.

Return type:

str

Returns:

the MAC, as a hexadecimal string.

verify(received_mac_tag)

Validate the binary MAC tag.

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 tampered with while in transit.

Parameters:

received_mac_tag (bytes/bytearray/memoryview) – This is the binary MAC, as received from the sender.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

Return type:

None

hexverify(hex_mac_tag)

Validate the printable MAC tag.

This method is like verify.

Parameters:

hex_mac_tag (string) – This is the printable MAC, as received from the sender.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

Return type:

None

encrypt_and_digest(plaintext, output=None)

Perform encrypt() and digest() in one step.

Parameters:

plaintext (bytes/bytearray/memoryview) – The piece of data to encrypt.

Keyword Arguments:

output (bytearray/memoryview) – The location where the ciphertext must be written to. If None, the ciphertext is returned.

Return type:

tuple[Optional[bytes], bytes]

Returns:

The tuple (ciphertext, tag), both bytes. The ciphertext is None when the output parameter specified a location for the result.

decrypt_and_verify(ciphertext, received_mac_tag, output=None)

Perform decrypt() and verify() in one step.

Parameters:
  • ciphertext (bytes/bytearray/memoryview) – The piece of data to decrypt.

  • received_mac_tag (bytes/bytearray/memoryview) – This is the binary MAC, as received from the sender.

Keyword Arguments:

output (bytearray/memoryview) – The location where the plaintext must be written to. If None, the plaintext is returned.

Return type:

Optional[bytes]

Returns:

the plaintext as bytes or None when the output parameter specified a location for the result.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

GCM mode

Galois/Counter Mode, defined in NIST SP 800-38D. It only works in combination with a 128 bits cipher like AES.

The new() function at the module level under Crypto.Cipher instantiates a new GCM cipher object for the relevant base algorithm.

Crypto.Cipher.<algorithm>.new(key, mode, *, nonce=None, mac_len=None, threads=1)

Create a new GCM object, using <algorithm> as the base block cipher.

Parameters:
  • key (bytes) – the cryptographic key

  • mode – the constant Crypto.Cipher.<algorithm>.MODE_GCM

  • nonce (bytes) – the value of the fixed nonce. It must be unique for the combination message/key. If not present, the library creates a random nonce (16 bytes long for AES).

  • mac_len (integer) – the desired length of the MAC tag, from 4 to 16 bytes (default: 16).

  • threads (int) – (only for AES) the maximum number of threads used to encrypt or decrypt long data (default: 1, no extra threads). Use 0 for as many threads as the CPU cores available to the process. See the same parameter for the CTR mode.

Returns:

a GCM cipher object (GcmMode)

The cipher object has a read-only attribute nonce.

With the threads parameter, both the encryption (CTR mode) and the authentication (GHASH) of long data run on several CPU cores.

Example (encryption):

>>> import json
>>> from base64 import b64encode
>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> header = b"header"
>>> data = b"secret"
>>> key = get_random_bytes(16)
>>> cipher = AES.new(key, AES.MODE_GCM)
>>> cipher.update(header)
>>> ciphertext, tag = cipher.encrypt_and_digest(data)
>>>
>>> json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
>>> json_v = [ b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag) ]
>>> result = json.dumps(dict(zip(json_k, json_v)))
>>> print(result)
{"nonce": "DpOK8NIOuSOQlTq+BphKWw==", "header": "aGVhZGVy", "ciphertext": "CZVqyacc", "tag": "B2tBgICbyw+Wji9KpLVa8w=="}

Example (decryption):

>>> import json
>>> from base64 import b64decode
>>> from Crypto.Cipher import AES
>>>
>>> # We assume that the key was securely shared beforehand
>>> try:
...     b64 = json.loads(json_input)
...     json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
...     jv = {k:b64decode(b64[k]) for k in json_k}
>>>
...     cipher = AES.new(key, AES.MODE_GCM, nonce=jv['nonce'])
...     cipher.update(jv['header'])
...     plaintext = cipher.decrypt_and_verify(jv['ciphertext'], jv['tag'])
...     print("The message was: " + plaintext.decode('utf-8'))
... except (ValueError, KeyError):
...     print("Incorrect decryption")

Note

GCM is most commonly used with 96-bit (12-byte) nonces, which is also the length recommended by NIST SP 800-38D.

If interoperability is important, one should take into account that the library default of a 128-bit random nonce may not be (easily) supported by other implementations. A 96-bit nonce can be explicitly generated for a new encryption cipher:

>>> key = get_random_bytes(16)
>>> nonce = get_random_bytes(12)
>>> cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)

GCM cipher object

class Crypto.Cipher._mode_gcm.GcmMode

Galois Counter Mode (GCM).

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 knows 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.

This mode is only available for ciphers that operate on 128 bits blocks (e.g. AES but not TDES).

See NIST SP800-38D.

nonce

The nonce originally used to create the object.

update(assoc_data)

Protect associated data

If there is any associated data, the caller has to invoke this function one or more times, before using decrypt or encrypt.

By associated data it is meant any data (e.g. packet headers) that will not be encrypted and will be transmitted in the clear. However, the receiver is still able to detect any modification to it. In GCM, the associated data is also called additional authenticated data (AAD).

If there is no associated data, this method must not be called.

The caller may split associated data in segments of any size, and invoke this method multiple times, each time with the next segment.

Parameters:

assoc_data (bytes/bytearray/memoryview) – A piece of associated data. There are no restrictions on its size.

Return type:

GcmMode

encrypt(plaintext, output=None)

Encrypt data with the key and the parameters set at initialization.

A cipher object is stateful: once you have encrypted a message you cannot encrypt (or decrypt) another message using the same object.

The data to encrypt can be broken up in two or more pieces and encrypt can be called multiple times.

That is, the statement:

>>> c.encrypt(a) + c.encrypt(b)

is equivalent to:

>>> c.encrypt(a+b)

This function does not add any padding to the plaintext.

Parameters:

plaintext (bytes/bytearray/memoryview) – The piece of data to encrypt. It can be of any length.

Keyword Arguments:

output (bytearray/memoryview) – The location where the ciphertext must be written to. If None, the ciphertext is returned.

Return type:

Optional[bytes]

Returns:

If output is None, the ciphertext as bytes. Otherwise, None.

decrypt(ciphertext, output=None)

Decrypt data with the key and the parameters set at initialization.

A cipher object is stateful: once you have decrypted a message you cannot decrypt (or encrypt) another message with the same object.

The data to decrypt can be broken up in two or more pieces and decrypt can be called multiple times.

That is, the statement:

>>> c.decrypt(a) + c.decrypt(b)

is equivalent to:

>>> c.decrypt(a+b)

This function does not remove any padding from the plaintext.

Parameters:

ciphertext (bytes/bytearray/memoryview) – The piece of data to decrypt. It can be of any length.

Keyword Arguments:

output (bytearray/memoryview) – The location where the plaintext must be written to. If None, the plaintext is returned.

Return type:

Optional[bytes]

Returns:

If output is None, the plaintext as bytes. Otherwise, None.

digest()

Compute the binary MAC tag in an AEAD mode.

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.

Return type:

bytes

Returns:

the MAC, as a byte string.

hexdigest()

Compute the printable MAC tag.

This method is like digest.

Return type:

str

Returns:

the MAC, as a hexadecimal string.

verify(received_mac_tag)

Validate the binary MAC tag.

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 tampered with while in transit.

Parameters:

received_mac_tag (bytes/bytearray/memoryview) – This is the binary MAC, as received from the sender.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

Return type:

None

hexverify(hex_mac_tag)

Validate the printable MAC tag.

This method is like verify.

Parameters:

hex_mac_tag (string) – This is the printable MAC, as received from the sender.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

Return type:

None

encrypt_and_digest(plaintext, output=None)

Perform encrypt() and digest() in one step.

Parameters:

plaintext (bytes/bytearray/memoryview) – The piece of data to encrypt.

Keyword Arguments:

output (bytearray/memoryview) – The location where the ciphertext must be written to. If None, the ciphertext is returned.

Return type:

tuple[Optional[bytes], bytes]

Returns:

The tuple (ciphertext, tag), both bytes. The ciphertext is None when the output parameter specified a location for the result.

decrypt_and_verify(ciphertext, received_mac_tag, output=None)

Perform decrypt() and verify() in one step.

Parameters:
  • ciphertext (bytes/bytearray/memoryview) – The piece of data to decrypt.

  • received_mac_tag (byte string) – This is the binary MAC, as received from the sender.

Keyword Arguments:

output (bytearray/memoryview) – The location where the plaintext must be written to. If None, the plaintext is returned.

Return type:

Optional[bytes]

Returns:

the plaintext as bytes or None when the output parameter specified a location for the result.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

SIV mode

Synthetic Initialization Vector (SIV), defined in RFC5297. It only works with ciphers with a block size of 128 bits (like AES).

Although less efficient than other modes, SIV is nonce misuse-resistant: accidental reuse of the nonce does not jeopardize the security as it happens with CCM or GCM. As a matter of fact, operating without a nonce is not an error per se: the cipher simply becomes deterministic. In other words, a message gets always encrypted into the same ciphertext.

The new() function at the module level under Crypto.Cipher instantiates a new SIV cipher object for the relevant base algorithm.

Crypto.Cipher.<algorithm>.new(key, mode, *, nonce=None)

Create a new SIV object, using <algorithm> as the base block cipher.

Parameters:
  • key (bytes) – the cryptographic key; it must be twice the size of the key required by the underlying cipher (e.g. 32 bytes for AES-128).

  • mode – the constant Crypto.Cipher.<algorithm>.MODE_SIV

  • nonce (bytes) – the value of the fixed nonce. It must be unique for the combination message/key. If not present, the encryption will be deterministic.

Returns:

a SIV cipher object (SivMode)

If the nonce parameter was provided to new(), the resulting cipher object has a read-only attribute nonce.

Example (encryption):

>>> import json
>>> from base64 import b64encode
>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> header = b"header"
>>> data = b"secret"
>>> key = get_random_bytes(16 * 2)
>>> nonce = get_random_bytes(16)
>>> cipher = AES.new(key, AES.MODE_SIV, nonce=nonce)    # Without nonce, the encryption
...                                                     # becomes deterministic
>>> cipher.update(header)
>>> ciphertext, tag = cipher.encrypt_and_digest(data)
>>>
>>> json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
>>> json_v = [ b64encode(x).decode('utf-8') for x in (nonce, header, ciphertext, tag) ]
>>> result = json.dumps(dict(zip(json_k, json_v)))
>>> print(result)
{"nonce": "zMiifAVvDpMS8hnGK/z+iw==", "header": "aGVhZGVy", "ciphertext": "Q7lReEAF", "tag": "KgdnBVbCee6B/wGmMf/wQA=="}

Example (decryption):

>>> import json
>>> from base64 import b64decode
>>> from Crypto.Cipher import AES
>>>
>>> # We assume that the key was securely shared beforehand
>>> try:
...     b64 = json.loads(json_input)
...     json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
...     jv = {k:b64decode(b64[k]) for k in json_k}
...
...     cipher = AES.new(key, AES.MODE_SIV, nonce=jv['nonce'])
...     cipher.update(jv['header'])
...     plaintext = cipher.decrypt_and_verify(jv['ciphertext'], jv['tag'])
...     print("The message was: " + plaintext.decode('utf-8'))
... except (ValueError, KeyError):
...     print("Incorrect decryption")

One side-effect is that encryption (or decryption) must take place in one go with the method encrypt_and_digest() (or decrypt_and_verify()). You cannot use encrypt() or decrypt(). The state diagram is therefore:

../../_images/siv.png

Fig. 4 State diagram for the SIV cipher mode

The length of the key passed to new() must be twice as required by the underlying block cipher (e.g. 32 bytes for AES-128).

Each call to the method update() consumes an full piece of associated data. That is, the sequence:

>>> siv_cipher.update(b"builtin")
>>> siv_cipher.update(b"securely")

is not equivalent to:

>>> siv_cipher.update(b"built")
>>> siv_cipher.update(b"insecurely")

SIV cipher object

class Crypto.Cipher._mode_siv.SivMode

Synthetic Initialization Vector (SIV).

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 knows the secret key. Additionally, decryption detects if any part of the message - including the header - has been modified or corrupted.

Unlike other AEAD modes such as CCM, EAX or GCM, accidental reuse of a nonce is not catastrophic for the confidentiality of the message. The only effect is that an attacker can tell when the same plaintext (and same associated data) is protected with the same key.

The length of the MAC is fixed to the block size of the underlying cipher. The key size is twice the length of the key of the underlying cipher.

This mode is only available for AES ciphers.

Cipher

SIV MAC size

(bytes)

SIV key length

(bytes)

AES-128

16

32

AES-192

16

48

AES-256

16

64

See RFC5297 and the original paper.

block_size

The block size of the underlying cipher, in bytes.

nonce

Public attribute is only available in case of non-deterministic encryption.

update(component)

Protect one associated data component

For SIV, the associated data is a sequence (vector) of non-empty byte strings (components).

This method consumes the next component. It must be called once for each of the components that constitue the associated data.

Note that the components have clear boundaries, so that:

>>> cipher.update(b"builtin")
>>> cipher.update(b"securely")

is not equivalent to:

>>> cipher.update(b"built")
>>> cipher.update(b"insecurely")

If there is no associated data, this method must not be called.

Parameters:

component (bytes/bytearray/memoryview) – The next associated data component.

Return type:

SivMode

digest()

Compute the binary MAC tag.

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.

Return type:

bytes

Returns:

the MAC, as a byte string.

hexdigest()

Compute the printable MAC tag.

This method is like digest.

Return type:

str

Returns:

the MAC, as a hexadecimal string.

verify(received_mac_tag)

Validate the binary MAC tag.

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 tampered with while in transit.

Parameters:

received_mac_tag (bytes/bytearray/memoryview) – This is the binary MAC, as received from the sender.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

Return type:

None

hexverify(hex_mac_tag)

Validate the printable MAC tag.

This method is like verify.

Parameters:

hex_mac_tag (string) – This is the printable MAC, as received from the sender.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

Return type:

None

encrypt_and_digest(plaintext, output=None)

Perform encrypt() and digest() in one step.

Parameters:

plaintext (bytes/bytearray/memoryview) – The piece of data to encrypt.

Keyword Arguments:

output (bytearray/memoryview) – The location where the ciphertext must be written to. If None, the ciphertext is returned.

Return type:

tuple[Optional[bytes], bytes]

Returns:

The tuple (ciphertext, tag), both bytes. The ciphertext is None when the output parameter specified a location for the result.

decrypt_and_verify(ciphertext, mac_tag, output=None)

Perform decryption and verification in one step.

A cipher object is stateful: once you have decrypted a message you cannot decrypt (or encrypt) another message with the same object.

You cannot reuse an object for encrypting or decrypting other data with the same key.

This function does not remove any padding from the plaintext.

Parameters:
  • ciphertext (bytes/bytearray/memoryview) – The piece of data to decrypt. It can be of any length.

  • mac_tag (bytes/bytearray/memoryview) – This is the binary MAC, as received from the sender.

Keyword Arguments:

output (bytearray/memoryview) – The location where the plaintext must be written to. If None, the plaintext is returned.

Return type:

Optional[bytes]

Returns:

the plaintext as bytes or None when the output parameter specified a location for the result.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

OCB mode

Offset CodeBook mode, a cipher designed by Rogaway and specified in RFC7253 (more specifically, this module implements the last variant, OCB3). It only works in combination with a 128 bits cipher like AES.

OCB was patented in USA but the author eventually abandoned the patents.

The new() function at the module level under Crypto.Cipher instantiates a new OCB cipher object for the relevant base algorithm.

Note

The OCB state machine is slightly different compared to other modes: if you encrypt (or decrypt) multiple chunks, at the end you MUST call the method encrypt (or decrypt) with no parameters. This last call will return any piece of internally cached ciphertext (or plaintext).

../../_images/ocb_mode.png

Fig. 5 State diagram for the OCB mode

Crypto.Cipher.<algorithm>.new(key, mode, *, nonce=None, mac_len=None, threads=1)

Create a new OCB object, using <algorithm> as the base block cipher.

Parameters:
  • key (bytes) – the cryptographic key

  • mode – the constant Crypto.Cipher.<algorithm>.MODE_OCB

  • nonce (bytes) – the value of the fixed nonce, with length between 1 and 15 bytes. It must be unique for the combination message/key. If not present, the library creates a 15 bytes random nonce.

  • mac_len (integer) – the desired length of the MAC tag (default if not present: 16 bytes).

  • threads (int) – (only for AES) the maximum number of threads used to encrypt or decrypt long data (default: 1, no extra threads). Use 0 for as many threads as the CPU cores available to the process. See the same parameter for the CTR mode.

Returns:

an OCB cipher object (OcbMode)

The cipher object has two read-only attributes: nonce and block_size.

With the threads parameter, the encryption and the decryption of long data run on several CPU cores (each block is processed independently of the others). The associated data is always processed by the calling thread.

Example (encryption as a once-off operation):

>>> import json
>>> from base64 import b64encode
>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> header = b"header"
>>> data = b"secret"
>>> key = get_random_bytes(16)
>>> cipher = AES.new(key, AES.MODE_OCB)
>>> cipher.update(header)
>>> ciphertext, tag = cipher.encrypt_and_digest(data)
>>>
>>> json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
>>> json_v = [ b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag) ]
>>> result = json.dumps(dict(zip(json_k, json_v)))
>>> print(result)
{"nonce": "I7E6PKxHNYo2i9sz8W98", "header": "aGVhZGVy", "ciphertext": "nYJnJ8jC", "tag": "0UbFcmO9lqGknCIDWRLALA=="}

Example (decryption as a once-off operation):

>>> import json
>>> from base64 import b64decode
>>> from Crypto.Cipher import AES
>>>
>>> # We assume that the key was securely shared beforehand
>>> try:
...     b64 = json.loads(json_input)
...     json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
...     jv = {k:b64decode(b64[k]) for k in json_k}
>>>
...     cipher = AES.new(key, AES.MODE_OCB, nonce=jv['nonce'])
...     cipher.update(jv['header'])
...     plaintext = cipher.decrypt_and_verify(jv['ciphertext'], jv['tag'])
...     print("The message was: " + plaintext.decode('utf-8'))
... except (ValueError, KeyError):
...     print("Incorrect decryption")

Example (encryption with multiple chunks):

>>> import json
>>> from base64 import b64encode
>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> header = b'header'
>>> data = [b'chunk1', b'chunk2', b'chunk3']
>>> key = get_random_bytes(16)
>>> cipher = AES.new(key, AES.MODE_OCB)
>>> cipher.update(header)
>>> ciphertext = b''
>>> for chunk in data:
...     ciphertext += cipher.encrypt(chunk)
>>> ciphertext += cipher.encrypt()
>>> tag = cipher.digest()
>>>
>>> json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
>>> json_v = [ b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag) ]
>>> result = json.dumps(dict(zip(json_k, json_v)))
>>> print(result)
{"nonce": "IABQ/ww8vGsu7F4sbHXK", "header": "aGVhZGVy", "ciphertext": "7Amm2DoiMHVkYC8dY7NEX86M", "tag": "qOPnjAxF63MOAx6xjwRvJQ=="}

Example (decryption with multiple chunks):

>>> import json
>>> from base64 import b64decode
>>> from Crypto.Cipher import AES
>>>
>>> # We assume that the key was securely shared beforehand
>>> try:
...     b64 = json.loads(json_input)
...     json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]
...     jv = {k:b64decode(b64[k]) for k in json_k}
>>>
...     cipher = AES.new(key, AES.MODE_OCB, nonce=jv['nonce'])
...     cipher.update(jv['header'])
...     ciphertext = jv['ciphertext']
>>>
...     # Split into small chunks, just for demo purposes
...     chunks = [ ciphertext[i:i+2] for i in range(0, len(ciphertext), 2) ]
>>>
...     plaintext = b''
...     for chunk in chunks:
...         plaintext += cipher.decrypt(chunk)
...     plaintext += cipher.decrypt()
...     cipher.verify(jv['tag'])
>>>
...     print("The message was: " + plaintext.decode('utf-8'))
... except (ValueError, KeyError):
...     print("Incorrect decryption")

OCB cipher object

class Crypto.Cipher._mode_ocb.OcbMode

Offset Codebook (OCB) mode.

block_size

The block size of the underlying cipher, in bytes.

nonce

The nonce originally used to create the object.

update(assoc_data)

Process the associated data.

If there is any associated data, the caller has to invoke this method one or more times, before using decrypt or encrypt.

By associated data it is meant any data (e.g. packet headers) that will not be encrypted and will be transmitted in the clear. However, the receiver shall still able to detect modifications.

If there is no associated data, this method must not be called.

The caller may split associated data in segments of any size, and invoke this method multiple times, each time with the next segment.

Parameters:

assoc_data (bytes/bytearray/memoryview) – A piece of associated data.

Return type:

OcbMode

encrypt(plaintext=None)

Encrypt the next piece of plaintext.

After the entire plaintext has been passed (but before digest), you must call this method one last time with no arguments to collect the final piece of ciphertext.

If possible, use the method encrypt_and_digest instead.

Parameters:

plaintext (bytes/bytearray/memoryview) – The next piece of data to encrypt or None to signify that encryption has finished and that any remaining ciphertext has to be produced.

Return type:

bytes

Returns:

the ciphertext, as a byte string. Its length may not match the length of the plaintext.

decrypt(ciphertext=None)

Decrypt the next piece of ciphertext.

After the entire ciphertext has been passed (but before verify), you must call this method one last time with no arguments to collect the remaining piece of plaintext.

If possible, use the method decrypt_and_verify instead.

Parameters:

ciphertext (bytes/bytearray/memoryview) – The next piece of data to decrypt or None to signify that decryption has finished and that any remaining plaintext has to be produced.

Return type:

bytes

Returns:

the plaintext, as a byte string. Its length may not match the length of the ciphertext.

digest()

Compute the binary MAC tag.

Call this method after the final encrypt (the one with no arguments) to obtain the MAC tag.

The MAC tag is needed by the receiver to determine authenticity of the message.

Return type:

bytes

Returns:

the MAC, as a byte string.

hexdigest()

Compute the printable MAC tag.

This method is like digest.

Return type:

str

Returns:

the MAC, as a hexadecimal string.

verify(received_mac_tag)

Validate the binary MAC tag.

Call this method after the final decrypt (the one with no arguments) to check if the message is authentic and valid.

Parameters:

received_mac_tag (bytes/bytearray/memoryview) – This is the binary MAC, as received from the sender.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

Return type:

None

hexverify(hex_mac_tag)

Validate the printable MAC tag.

This method is like verify.

Parameters:

hex_mac_tag (string) – This is the printable MAC, as received from the sender.

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

Return type:

None

encrypt_and_digest(plaintext)

Encrypt the message and create the MAC tag in one step.

Parameters:

plaintext (bytes/bytearray/memoryview) – The entire message to encrypt.

Return type:

tuple[bytes, bytes]

Returns:

The tuple (ciphertext, tag), both bytes.

decrypt_and_verify(ciphertext, received_mac_tag)

Decrypted the message and verify its authenticity in one step.

Parameters:
  • ciphertext (bytes/bytearray/memoryview) – The entire message to decrypt.

  • received_mac_tag (byte string) – This is the binary MAC, as received from the sender.

Return type:

bytes

Returns:

the decrypted data (byte string).

Raises:

ValueError – if the MAC does not match. The message has been tampered with or the key is incorrect.

KW mode

Key Wrapping (or NIST SP 800-38F) is an ad-hoc authenticated cipher mode designed to wrap cryptographic keys, and not other types of data. It is deterministic (it doesn’t use nonces or IVs) and only works with ciphers with a block size of 128 bits (like AES).

The cryptographic key to wrap must have a length multiple of 8.

There is no reason to prefer this ad-hoc mode to one like SIV.

The new() function at the module level under Crypto.Cipher instantiates a new KW cipher object for the relevant base algorithm. In the following definition, <algorithm> can only be AES today:

Crypto.Cipher.<algorithm>.new(key, <algorithm>.MODE_KW)

Create a new Key Wrapping (KW) cipher object, using <algorithm> as the base block cipher.

Parameters:

key (bytes) – the cryptographic key

Returns:

a KW cipher object (KWMode)

The cipher object has one read-only attribute block_size, and two methods (seal() and unseal()).

Example of encryption:

>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> key_to_wrap = b"1234567890123456"
>>> wrapping_key = get_random_bytes(16)
>>> cipher = AES.new(wrapping_key, AES.MODE_KW)
>>> ciphertext = cipher.seal(key_to_wrap)

Example of decryption:

>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> wrapping_key = get_random_bytes(16)
>>> cipher = AES.new(wrapping_key, AES.MODE_KW)
>>> try:
...     unwrapped_key = cipher.unseal(ciphertext)
... except ValueError:
...     print("Invalid decryption")

KW cipher object

class Crypto.Cipher._mode_kw.KWMode

Key Wrap (KW) mode.

This is a deterministic Authenticated Encryption (AE) mode for protecting cryptographic keys. See NIST SP800-38F.

It provides both confidentiality and authenticity, and it designed so that any bit of the ciphertext depends on all bits of the plaintext.

This mode is only available for ciphers that operate on 128 bits blocks (e.g., AES).

block_size

The block size of the underlying cipher, in bytes.

seal(plaintext)

Encrypt and authenticate (wrap) a cryptographic key.

Parameters:

plaintext (Union[bytes, bytearray]) – The cryptographic key to wrap. It must be at least 16 bytes long, and its length must be a multiple of 8.

Return type:

bytes

Returns:

The wrapped key.

unseal(ciphertext)

Decrypt and authenticate (unwrap) a cryptographic key.

Parameters:

ciphertext (Union[bytes, bytearray]) – The cryptographic key to unwrap. It must be at least 24 bytes long, and its length must be a multiple of 8.

Return type:

bytes

Returns:

The original key.

Raises: ValueError

If the ciphertext or the key are not valid.

KWP mode

Key Wrapping with Padding (or NIST SP 800-38F) is an ad-hoc authenticated cipher mode designed to wrap cryptographic keys, and not other types of data. It is deterministic (it doesn’t use nonces or IVs) and only works with ciphers with a block size of 128 bits (like AES).

The cryptographic key to wrap can have any length.

There is no reason to prefer this ad-hoc mode to one like SIV.

The new() function at the module level under Crypto.Cipher instantiates a new KWP cipher object for the relevant base algorithm. In the following definition, <algorithm> can only be AES today:

Crypto.Cipher.<algorithm>.new(key, <algorithm>.MODE_KWP)

Create a new Key Wrapping with Padding (KWP) cipher object, using <algorithm> as the base block cipher.

Parameters:

key (bytes) – the cryptographic key

Returns:

a KWP cipher object (KWPMode)

The cipher object has one read-only attribute block_size, and two methods (seal() and unseal()).

Example of encryption:

>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> key_to_wrap = b"1234567890123456"
>>> wrapping_key = get_random_bytes(16)
>>> cipher = AES.new(wrapping_key, AES.MODE_KWP)
>>> ciphertext = cipher.seal(key_to_wrap)

Example of decryption:

>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> wrapping_key = get_random_bytes(16)
>>> cipher = AES.new(wrapping_key, AES.MODE_KWP)
>>> try:
...     unwrapped_key = cipher.unseal(ciphertext)
... except ValueError:
...     print("Invalid decryption")

KWP cipher object

class Crypto.Cipher._mode_kwp.KWPMode

Key Wrap with Padding (KWP) mode.

This is a deterministic Authenticated Encryption (AE) mode for protecting cryptographic keys. See NIST SP800-38F.

It provides both confidentiality and authenticity, and it designed so that any bit of the ciphertext depends on all bits of the plaintext.

This mode is only available for ciphers that operate on 128 bits blocks (e.g., AES).

block_size

The block size of the underlying cipher, in bytes.

seal(plaintext)

Encrypt and authenticate (wrap) a cryptographic key.

Parameters:

plaintext (Union[bytes, bytearray]) – The cryptographic key to wrap.

Return type:

bytes

Returns:

The wrapped key.

unseal(ciphertext)

Decrypt and authenticate (unwrap) a cryptographic key.

Parameters:

ciphertext (Union[bytes, bytearray]) – The cryptographic key to unwrap. It must be at least 16 bytes long, and its length must be a multiple of 8.

Return type:

bytes

Returns:

The original key.

Raises: ValueError

If the ciphertext or the key are not valid.