File: secret.rst

package info (click to toggle)
python-nacl 1.5.0-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 14,776 kB
  • sloc: ansic: 45,889; python: 7,249; sh: 6,752; asm: 2,974; makefile: 1,011; cs: 35; xml: 30; pascal: 11
file content (185 lines) | stat: -rw-r--r-- 7,006 bytes parent folder | download | duplicates (3)
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
Secret Key Encryption
=====================

.. currentmodule:: nacl.secret

Secret key encryption (also called symmetric key encryption) is analogous to a
safe. You can store something secret through it and anyone who has the key can
open it and view the contents. :class:`~nacl.secret.SecretBox` functions as
just such a safe, and like any good safe any attempts to tamper with the
contents are easily detected.

Secret key encryption allows you to store or transmit data over insecure
channels without leaking the contents of that message, nor anything about it
other than the length.

Example
-------

.. testcode::

    import nacl.secret
    import nacl.utils

    # This must be kept secret, this is the combination to your safe
    key = nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)

    # This is your safe, you can use it to encrypt or decrypt messages
    box = nacl.secret.SecretBox(key)

    # This is our message to send, it must be a bytestring as SecretBox will
    #   treat it as just a binary blob of data.
    message = b"The president will be exiting through the lower levels"

PyNaCl can automatically generate a random nonce for us, making the encryption
very simple:

.. testcode::

    # Encrypt our message, it will be exactly 40 bytes longer than the
    #   original message as it stores authentication information and the
    #   nonce alongside it.
    encrypted = box.encrypt(message)

    assert len(encrypted) == len(message) + box.NONCE_SIZE + box.MACBYTES

However, if we need to use an explicit nonce, it can be passed along with the
message:

.. testcode::

    # This is a nonce, it *MUST* only be used once, but it is not considered
    #   secret and can be transmitted or stored alongside the ciphertext. A
    #   good source of nonces are just sequences of 24 random bytes.
    nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)

    encrypted = box.encrypt(message, nonce)

If you need to get the ciphertext and the authentication data
without the nonce, you can get the `ciphertext` attribute of the
:class:`~nacl.utils.EncryptedMessage` instance returned by
:meth:`~nacl.secret.SecretBox.encrypt`:

.. testcode::

    nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)

    encrypted = box.encrypt(message, nonce)

    # since we are transmitting the nonce by some other means,
    # we just need to get the ciphertext and authentication data

    ctext = encrypted.ciphertext

    # ctext is just nacl.secret.SecretBox.MACBYTES longer
    # than the original message

    assert len(ctext) == len(message) + box.MACBYTES

Finally, the message is decrypted (regardless of how the nonce was generated):

.. testcode::

    # Decrypt our message, an exception will be raised if the encryption was
    #   tampered with or there was otherwise an error.
    plaintext = box.decrypt(encrypted)
    print(plaintext.decode('utf-8'))

.. testoutput::

    The president will be exiting through the lower levels


Requirements
------------

Key
~~~

The 32 bytes key given to :class:`~nacl.secret.SecretBox` must be kept secret.
It is the combination to your "safe" and anyone with this key will be able to
decrypt the data, or encrypt new data.


Nonce
~~~~~

The 24-byte nonce (`Number used once <https://en.wikipedia.org/wiki/Cryptographic_nonce>`_)
given to :meth:`~nacl.secret.SecretBox.encrypt` and
:meth:`~nacl.secret.SecretBox.decrypt` must **NEVER** be reused for a
particular key. Reusing a nonce may give an attacker enough information to
decrypt or forge other messages. A nonce is not considered secret and may be
freely transmitted or stored in plaintext alongside the ciphertext.

A nonce does not need to be random or unpredictable, nor does the method of
generating them need to be secret. A nonce could simply be a counter
incremented with each message encrypted, which can be useful in
connection-oriented protocols to reject duplicate messages ("replay
attacks"). A bidirectional connection could use the same key for both
directions, as long as their nonces never overlap (e.g. one direction always
sets the high bit to "1", the other always sets it to "0").

If you use a counter-based nonce along with a key that is persisted from one
session to another (e.g. saved to disk), you must store the counter along
with the key, to avoid accidental nonce reuse on the next session. For this
reason, many protocols derive a new key for each session, reset the counter
to zero with each new key, and never store the derived key or the counter.

You can safely generate random nonces by calling
:func:`~nacl.utils.random` with ``SecretBox.NONCE_SIZE``.


Reference
---------

.. class:: SecretBox(key, encoder)

    The SecretBox class encrypts and decrypts messages using the given secret
    key.

    The ciphertexts generated by :class:`~nacl.secret.Secretbox` include a 16
    byte authenticator which is checked as part of the decryption. An invalid
    authenticator will cause the decrypt function to raise an exception. The
    authenticator is not a signature. Once you've decrypted the message you've
    demonstrated the ability to create arbitrary valid message, so messages you
    send are repudiable. For non-repudiable messages, sign them after
    encryption.

    :param bytes key: The secret key used to encrypt and decrypt messages.
    :param encoder: A class that is able to decode the ``key``.

    .. method:: encrypt(plaintext, nonce, encoder)

        Encrypts the plaintext message using the given `nonce` (or generates
        one randomly if omitted) and returns the ciphertext encoded with the
        encoder.

        .. warning:: It is **VITALLY** important that the nonce is a nonce,
            i.e. it is a number used only once for any given key. If you fail
            to do this, you compromise the privacy of the messages encrypted.
            Give your nonces a different prefix, or have one side use an odd
            counter and one an even counter. Just make sure they are different.

        :param bytes plaintext: The plaintext message to encrypt.
        :param bytes nonce: The nonce to use in the encryption.
        :param encoder:  A class that is able to decode the ciphertext.

        :return: An instance of :class:`~nacl.utils.EncryptedMessage`.

    .. method:: decrypt(ciphertext, nonce, encoder)

        Decrypts the ciphertext using the `nonce` (explicitly, when passed as a
        parameter or implicitly, when omitted, as part of the ciphertext) and
        returns the plaintext message.

        :param bytes ciphertext: The encrypted message to decrypt.
        :param bytes nonce: The nonce to use in the decryption.
        :param encoder: A class that is able to decode the plaintext.

        :return bytes: The decrypted plaintext.

Algorithm details
-----------------

:Encryption: `XSalsa20 stream cipher <https://libsodium.gitbook.io/doc/advanced/stream_ciphers/xsalsa20>`_
:Authentication: `Poly1305 MAC <https://en.wikipedia.org/wiki/Poly1305-AES>`_