mirror of
https://github.com/Legrandin/pycryptodome.git
synced 2025-12-08 05:19:46 +00:00
Dedicated tests for SIV
This commit is contained in:
parent
97bac28d7e
commit
5c25517a26
6 changed files with 433 additions and 325 deletions
|
|
@ -34,9 +34,9 @@ Synthetic Initialization Vector (SIV) mode.
|
|||
|
||||
__all__ = ['SivMode']
|
||||
|
||||
from binascii import unhexlify, hexlify
|
||||
from binascii import hexlify
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
from Crypto.Util.py3compat import byte_string, bord, unhexlify
|
||||
|
||||
from Crypto.Util import Counter
|
||||
from Crypto.Util.number import long_to_bytes, bytes_to_long
|
||||
|
|
@ -57,12 +57,6 @@ class SivMode(object):
|
|||
Additionally, decryption detects if any part of the message - including the
|
||||
header - has been modified or corrupted.
|
||||
|
||||
If the data being encrypted is completely unpredictable to an adversary
|
||||
(e.g. a secret key, for key wrapping purposes) a nonce is not strictly
|
||||
required.
|
||||
|
||||
Otherwise, a *nonce* has to be provided.
|
||||
|
||||
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
|
||||
|
|
@ -91,45 +85,24 @@ class SivMode(object):
|
|||
.. __: http://www.cs.ucdavis.edu/~rogaway/papers/keywrap.pdf
|
||||
"""
|
||||
|
||||
def __init__(self, factory, **kwargs):
|
||||
"""Create a new block cipher, configured in
|
||||
Synthetic Initializaton Vector (SIV) mode.
|
||||
|
||||
:Parameters:
|
||||
factory : object
|
||||
A symmetric cipher module from `Crypto.Cipher`
|
||||
(like `Crypto.Cipher.AES`).
|
||||
:Keywords:
|
||||
key : byte string
|
||||
The secret key to use in the symmetric cipher.
|
||||
It must be 32, 48 or 64 bytes long.
|
||||
If AES is the chosen cipher, the variants *AES-128*,
|
||||
*AES-192* and or *AES-256* will be used internally.
|
||||
nonce : byte string
|
||||
A mandatory value that must never be reused for any other encryption.
|
||||
There are no restrictions on its length,
|
||||
but it is recommended to use at least 16 bytes.
|
||||
|
||||
The nonce shall never repeat for two different messages encrypted
|
||||
with the same key, but it does not need to be random.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, key, nonce, kwargs):
|
||||
self.block_size = factory.block_size
|
||||
self._factory = factory
|
||||
|
||||
try:
|
||||
self._key = key = kwargs.pop("key")
|
||||
except KeyError, e:
|
||||
raise TypeError("Missing parameter: " + str(e))
|
||||
self.nonce = nonce
|
||||
self._cipher_params = kwargs
|
||||
|
||||
self._nonce = kwargs.pop("nonce", None)
|
||||
if len(key) not in (32, 48, 64):
|
||||
raise ValueError("Incorrect key length (%d bytes)" % len(key))
|
||||
|
||||
self._cipher_params = dict(kwargs)
|
||||
if nonce is not None:
|
||||
if not byte_string(nonce):
|
||||
raise TypeError("When provided, the nonce must be a byte string")
|
||||
|
||||
if len(nonce) == 0:
|
||||
raise ValueError("When provided, the nonce must be non-empty")
|
||||
|
||||
subkey_size = len(key) // 2
|
||||
if len(key) & 1:
|
||||
raise ValueError("MODE_SIV requires a key twice as long as"
|
||||
" for the underlying cipher")
|
||||
|
||||
self._mac_tag = None # Cache for MAC tag
|
||||
self._kdf = _S2V(key[:subkey_size],
|
||||
|
|
@ -137,6 +110,9 @@ class SivMode(object):
|
|||
cipher_params=self._cipher_params)
|
||||
self._subkey_cipher = key[subkey_size:]
|
||||
|
||||
# Purely for the purpose of verifying that cipher_params are OK
|
||||
factory.new(key[:subkey_size], factory.MODE_ECB, **kwargs)
|
||||
|
||||
# Allowed transitions after initialization
|
||||
self._next = [self.update, self.encrypt, self.decrypt,
|
||||
self.digest, self.verify]
|
||||
|
|
@ -155,25 +131,30 @@ class SivMode(object):
|
|||
counter=ctr,
|
||||
**self._cipher_params)
|
||||
|
||||
def update(self, assoc_data):
|
||||
"""Protect associated data
|
||||
def update(self, component):
|
||||
"""Protect one associated data component
|
||||
|
||||
If there is any associated data, the caller has to invoke
|
||||
this function one or more times, before using
|
||||
``decrypt`` or ``encrypt``.
|
||||
For SIV, the associated data is a sequence (*vector*) of non-empty
|
||||
byte strings (*components*).
|
||||
|
||||
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.
|
||||
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")
|
||||
>>> c.update(b"insecurely")
|
||||
|
||||
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 : byte string
|
||||
A piece of associated data. There are no restrictions on its size.
|
||||
component : byte string
|
||||
The next associated data component. It must not be empty.
|
||||
"""
|
||||
|
||||
if self.update not in self._next:
|
||||
|
|
@ -183,7 +164,7 @@ class SivMode(object):
|
|||
self._next = [self.update, self.encrypt, self.decrypt,
|
||||
self.digest, self.verify]
|
||||
|
||||
return self._kdf.update(assoc_data)
|
||||
return self._kdf.update(component)
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
"""Encrypt data with the key and the parameters set at initialization.
|
||||
|
|
@ -202,7 +183,7 @@ class SivMode(object):
|
|||
:Parameters:
|
||||
plaintext : byte string
|
||||
The piece of data to encrypt.
|
||||
It can be of any length.
|
||||
It can be of any length, but it cannot be empty.
|
||||
:Return:
|
||||
the encrypted data, as a byte string.
|
||||
It is as long as *plaintext*.
|
||||
|
|
@ -214,8 +195,8 @@ class SivMode(object):
|
|||
|
||||
self._next = [self.digest]
|
||||
|
||||
if self._nonce:
|
||||
self._kdf.update(self._nonce)
|
||||
if self.nonce:
|
||||
self._kdf.update(self.nonce)
|
||||
self._kdf.update(plaintext)
|
||||
|
||||
self._mac_tag = self._kdf.derive()
|
||||
|
|
@ -361,8 +342,8 @@ class SivMode(object):
|
|||
|
||||
plaintext = self._cipher.decrypt(ciphertext)
|
||||
|
||||
if self._nonce:
|
||||
self._kdf.update(self._nonce)
|
||||
if self.nonce:
|
||||
self._kdf.update(self.nonce)
|
||||
if plaintext:
|
||||
self._kdf.update(plaintext)
|
||||
|
||||
|
|
@ -371,4 +352,36 @@ class SivMode(object):
|
|||
|
||||
|
||||
def _create_siv_cipher(factory, **kwargs):
|
||||
return SivMode(factory, **kwargs)
|
||||
"""Create a new block cipher, configured in
|
||||
Synthetic Initializaton Vector (SIV) mode.
|
||||
|
||||
:Parameters:
|
||||
|
||||
factory : object
|
||||
A symmetric cipher module from `Crypto.Cipher`
|
||||
(like `Crypto.Cipher.AES`).
|
||||
|
||||
:Keywords:
|
||||
|
||||
key : byte string
|
||||
The secret key to use in the symmetric cipher.
|
||||
It must be 32, 48 or 64 bytes long.
|
||||
If AES is the chosen cipher, the variants *AES-128*,
|
||||
*AES-192* and or *AES-256* will be used internally.
|
||||
|
||||
nonce : byte string
|
||||
For deterministic encryption, it is not present.
|
||||
|
||||
Otherwise, it is value that must never be reused.
|
||||
There are no restrictions on its length,
|
||||
but it is recommended to use at least 16 bytes.
|
||||
"""
|
||||
|
||||
try:
|
||||
key = kwargs.pop("key")
|
||||
except KeyError, e:
|
||||
raise TypeError("Missing parameter: " + str(e))
|
||||
|
||||
nonce = kwargs.pop("nonce", None)
|
||||
|
||||
return SivMode(factory, key, nonce, kwargs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue