-
Notifications
You must be signed in to change notification settings - Fork 522
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for TurboSHAKE128 and TurboSHAKE256
- Loading branch information
Showing
16 changed files
with
724 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
TurboSHAKE128 and TurboSHAKE256 | ||
=============================== | ||
|
||
TurboSHAKE is a family of *eXtendable-Output Functions* (XOFs) based on the Keccak permutation, | ||
which is also the basis for SHA-3. | ||
|
||
A XOF is a generalization of a cryptographic hash. | ||
The output digest of a XOF can take any length, as required by the caller, | ||
unlike SHA-256 (for instance) that always produces exactly 32 bytes. | ||
The output bits of a XOF do **not** depend on the output length, | ||
which means that the output length | ||
does not even need to be known (or declared) when the XOF is created. | ||
|
||
Therefore, a XOF object has a ``read(N: int)`` method (much like a file object) | ||
instead of a ``digest()`` method. ``read()`` can be called any number of times, | ||
and it will return different bytes each time. | ||
|
||
.. figure:: xof.png | ||
:align: center | ||
:figwidth: 50% | ||
|
||
Generic state diagram for a XOF object | ||
|
||
The TurboSHAKE family is not standardized. However, an RFC_ is being written. | ||
It comprises of two members: | ||
|
||
.. csv-table:: | ||
:header: Name, (2nd) Pre-image strength, Collision strength | ||
|
||
TurboSHAKE128, "128 bits (output >= 16 bytes)", "256 bits (output >= 32 bytes)" | ||
TurboSHAKE256, "256 bits (output >= 32 bytes)", "512 bits (output >= 64 bytes)" | ||
|
||
In addition to hashing, TurboSHAKE allows for domain separation | ||
via a *domain separation byte* (that is, the ``domain`` parameter to :func:`Crypto.Hash.TurboSHAKE128.new` | ||
and to :func:`Crypto.Hash.TurboSHAKE256.new`). | ||
|
||
.. hint:: | ||
|
||
For instance, if you are using TurboSHAKE in two applications, | ||
by picking different domain separation bytes you can ensure | ||
that they will never end up using the same digest in practice. | ||
The important factor is that the strings are different; | ||
the actual value of the domain separation byte is irrelevant. | ||
|
||
In the following example, we extract 26 bytes (208 bits) from the TurboSHAKE128 XOF:: | ||
|
||
>>> from Crypto.Hash import TurboSHAKE128 | ||
>>> | ||
>>> xof = TurboSHAKE128.new() | ||
>>> xof.update(b'Some data') | ||
>>> print(xof.read(26).hex()) | ||
d9dfade4ff8be344749908073916d3abd185ef88f5401024f029 | ||
|
||
.. _RFC: https://datatracker.ietf.org/doc/draft-irtf-cfrg-kangarootwelve/ | ||
|
||
.. automodule:: Crypto.Hash.TurboSHAKE128 | ||
:members: | ||
|
||
.. automodule:: Crypto.Hash.TurboSHAKE256 | ||
:members: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
from Crypto.Util._raw_api import (VoidPointer, SmartPointer, | ||
create_string_buffer, | ||
get_raw_buffer, c_size_t, | ||
c_uint8_ptr, c_ubyte) | ||
|
||
from Crypto.Util.number import long_to_bytes | ||
from Crypto.Util.py3compat import bchr | ||
|
||
from .keccak import _raw_keccak_lib | ||
|
||
|
||
class TurboSHAKE(object): | ||
"""A TurboSHAKE hash object. | ||
Do not instantiate directly. | ||
Use the :func:`new` function. | ||
""" | ||
|
||
def __init__(self, capacity, domain_separation, data): | ||
|
||
state = VoidPointer() | ||
result = _raw_keccak_lib.keccak_init(state.address_of(), | ||
c_size_t(capacity), | ||
c_ubyte(12)) # Reduced number of rounds | ||
if result: | ||
raise ValueError("Error %d while instantiating TurboSHAKE" | ||
% result) | ||
self._state = SmartPointer(state.get(), _raw_keccak_lib.keccak_destroy) | ||
|
||
self._is_squeezing = False | ||
self._capacity = capacity | ||
self._domain = domain_separation | ||
|
||
if data: | ||
self.update(data) | ||
|
||
|
||
def update(self, data): | ||
"""Continue hashing of a message by consuming the next chunk of data. | ||
Args: | ||
data (byte string/byte array/memoryview): The next chunk of the message being hashed. | ||
""" | ||
|
||
if self._is_squeezing: | ||
raise TypeError("You cannot call 'update' after the first 'read'") | ||
|
||
result = _raw_keccak_lib.keccak_absorb(self._state.get(), | ||
c_uint8_ptr(data), | ||
c_size_t(len(data))) | ||
if result: | ||
raise ValueError("Error %d while updating TurboSHAKE state" | ||
% result) | ||
return self | ||
|
||
def read(self, length): | ||
""" | ||
Compute the next piece of XOF output. | ||
.. note:: | ||
You cannot use :meth:`update` anymore after the first call to | ||
:meth:`read`. | ||
Args: | ||
length (integer): the amount of bytes this method must return | ||
:return: the next piece of XOF output (of the given length) | ||
:rtype: byte string | ||
""" | ||
|
||
self._is_squeezing = True | ||
bfr = create_string_buffer(length) | ||
result = _raw_keccak_lib.keccak_squeeze(self._state.get(), | ||
bfr, | ||
c_size_t(length), | ||
c_ubyte(self._domain)) | ||
if result: | ||
raise ValueError("Error %d while extracting from TurboSHAKE" | ||
% result) | ||
|
||
return get_raw_buffer(bfr) | ||
|
||
def new(self, data=None): | ||
return type(self)(self._capacity, self._domain, data) | ||
|
||
def _reset(self): | ||
result = _raw_keccak_lib.keccak_reset(self._state.get()) | ||
if result: | ||
raise ValueError("Error %d while resetting TurboSHAKE state" | ||
% result) | ||
self._is_squeezing = False | ||
|
||
|
||
def new(**kwargs): | ||
"""Create a new TurboSHAKE128 object. | ||
Args: | ||
domain (integer): | ||
Optional - A domain separation byte, between 0x01 and 0x7F. | ||
The default value is 0x1F. | ||
data (bytes/bytearray/memoryview): | ||
Optional - The very first chunk of the message to hash. | ||
It is equivalent to an early call to :meth:`update`. | ||
:Return: A :class:`TurboSHAKE` object | ||
""" | ||
|
||
domain_separation = kwargs.get('domain', 0x1F) | ||
if not (0x01 <= domain_separation <= 0x7F): | ||
raise ValueError("Incorrect domain separation value (%d)" % | ||
domain_separation) | ||
data = kwargs.get('data') | ||
return TurboSHAKE(32, domain_separation, data=data) |
Oops, something went wrong.