1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
Hashing
=======
.. currentmodule:: nacl.hash
Cryptographic secure hash functions are irreversible transforms
of input data to a fixed length `digest`.
The standard properties of a cryptographic hash make these functions useful
both for standalone usage as data integrity checkers, as well as ``black-box``
building blocks of other kind of algorithms and data structures.
All of the hash functions exposed in :py:mod:`nacl.hash` can be used
as data integrity checkers.
Integrity check examples
------------------------
Message's creator perspective (:py:func:`~nacl.hash.sha256`,
:py:func:`~nacl.hash.sha512`,
:py:func:`~nacl.hash.blake2b`)
.. testcode::
import nacl.encoding
import nacl.hash
HASHER = nacl.hash.sha256
# could be nacl.hash.sha512 or nacl.hash.blake2b instead
# define a 1024 bytes log message
msg = 16*b'256 BytesMessage'
digest = HASHER(msg, encoder=nacl.encoding.HexEncoder)
# now send msg and digest to the user
print(nacl.encoding.HexEncoder.encode(msg))
print(digest)
.. testoutput::
b'3235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d657373616765'
b'12b413c70c148d79bb57a1542156c5f35e24ad77c38e8c0e776d055e827cdd45'
Message's user perspective (:py:func:`~nacl.hash.sha256`,
:py:func:`~nacl.hash.sha512`,
:py:func:`~nacl.hash.blake2b`)
.. testcode::
from nacl.bindings.utils import sodium_memcmp
import nacl.encoding
import nacl.hash
HASHER = nacl.hash.sha256
# could be nacl.hash.sha512 or nacl.hash.blake2b instead
# we received a 1024 bytes long message and it hex encoded digest
received_msg = nacl.encoding.HexEncoder.decode(
b'3235362042797465734d6573736167653235362042797465734d657373616765'
b'3235362042797465734d6573736167653235362042797465734d657373616765'
b'3235362042797465734d6573736167653235362042797465734d657373616765'
b'3235362042797465734d6573736167653235362042797465734d657373616765'
b'3235362042797465734d6573736167653235362042797465734d657373616765'
b'3235362042797465734d6573736167653235362042797465734d657373616765'
b'3235362042797465734d6573736167653235362042797465734d657373616765'
b'3235362042797465734d6573736167653235362042797465734d657373616765'
)
dgst = b'12b413c70c148d79bb57a1542156c5f35e24ad77c38e8c0e776d055e827cdd45'
shortened = received_msg[:-1]
modified = b'modified' + received_msg[:-8]
orig_dgs = HASHER(received_msg, encoder=nacl.encoding.HexEncoder)
shrt_dgs = HASHER(shortened, encoder=nacl.encoding.HexEncoder)
mdfd_dgs = HASHER(modified, encoder=nacl.encoding.HexEncoder)
def eq_chk(dgs0, dgs1):
if sodium_memcmp(dgs0, dgs1):
return 'equals'
return 'is different from'
MSG = 'Digest of {0} message {1} original digest'
for chk in (('original', orig_dgs),
('truncated', shrt_dgs),
('modified', mdfd_dgs)):
print(MSG.format(chk[0], eq_chk(dgst, chk[1])))
.. testoutput::
Digest of original message equals original digest
Digest of truncated message is different from original digest
Digest of modified message is different from original digest
Additional hashing usages for :class:`~nacl.hash.blake2b`
---------------------------------------------------------
As already hinted above, traditional cryptographic hash functions can be used
as building blocks for other uses, typically combining a secret-key with
the message via some construct like the ``HMAC`` one.
The :class:`~nacl.hash.blake2b` hash function can be used directly both
for message authentication and key derivation, replacing the ``HMAC`` construct
and the ``HKDF`` one by setting the additional parameters ``key``, ``salt``
and ``person``.
Please note that **key stretching procedures** like ``HKDF`` or
the one outlined in `Key derivation`_ are **not** suited to derive
a *cryptographically-strong* key from a *low-entropy input* like a plain-text
password or to compute a strong *long-term stored* hash used as password
verifier. See the :ref:`password-hashing` section for some more informations
and usage examples of the password hashing constructs provided in
:py:mod:`~nacl.pwhash`.
Message authentication
----------------------
To authenticate a message, using a secret key, the blake2b function
must be called as in the following example.
Message authentication example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. testcode::
import nacl.encoding
import nacl.utils
from nacl.hash import blake2b
msg = 16*b'256 BytesMessage'
msg2 = 16*b'256 bytesMessage'
auth_key = nacl.utils.random(size=64)
# the simplest way to get a cryptographic quality auth_key
# is to generate it with a cryptographic quality
# random number generator
auth1_key = nacl.utils.random(size=64)
# generate a different key, just to show the mac is changed
# both with changing messages and with changing keys
mac0 = blake2b(msg, key=auth_key, encoder=nacl.encoding.HexEncoder)
mac1 = blake2b(msg, key=auth1_key, encoder=nacl.encoding.HexEncoder)
mac2 = blake2b(msg2, key=auth_key, encoder=nacl.encoding.HexEncoder)
for i, mac in enumerate((mac0, mac1, mac2)):
print('Mac{0} is: {1}.'.format(i, mac))
.. testoutput::
Mac0 is: b'...'.
Mac1 is: b'...'.
Mac2 is: b'...'.
Key derivation
--------------
The blake2b algorithm can replace a key derivation function by
following the lines of:
Key derivation example
~~~~~~~~~~~~~~~~~~~~~~
.. testcode::
import nacl.encoding
import nacl.utils
from nacl.hash import blake2b
master_key = nacl.utils.random(64)
derivation_salt = nacl.utils.random(16)
personalization = b'<DK usage>'
derived = blake2b(b'', key=master_key, salt=derivation_salt,
person=personalization,
encoder=nacl.encoding.RawEncoder)
By repeating the key derivation procedure before encrypting our messages,
and sending the derivation_salt along with the encrypted message, we can
expect to never reuse a key, drastically reducing the risks which ensue from
such a reuse.
|