Hashing

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 nacl.hash can be used as data integrity checkers.

Integrity check examples

Message’s creator perspective (sha256(),

sha512(), blake2b())

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)
b'3235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d6573736167653235362042797465734d657373616765'
b'12b413c70c148d79bb57a1542156c5f35e24ad77c38e8c0e776d055e827cdd45'
Message’s user perspective (sha256(),

sha512(), blake2b())

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])))
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 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 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 Password hashing section for some more informations and usage examples of the password hashing constructs provided in 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

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))
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

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.