File: hashing.rst

package info (click to toggle)
python-nacl 1.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 14,408 kB
  • sloc: ansic: 41,345; sh: 16,028; python: 5,152; asm: 4,264; makefile: 1,136; xml: 30; pascal: 10
file content (189 lines) | stat: -rw-r--r-- 6,685 bytes parent folder | download | duplicates (4)
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.