Secret Key Encryption

Secret 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. SecretBox functions as just such a safe, and like any good safe any attempts to tamper with the contents is 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.


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 is as just a binary blob of data.
message = b"The president will be exiting through the lower levels"

# 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 nonce is just 24 random bytes.
nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)

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

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



The 32 bytes key given to 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.


The 24 bytes nonce (Number used once) given to encrypt() and decrypt() must NEVER be reused for a particular key. Reusing the nonce means an attacker will have enough information to recover your secret key and encrypt or decrypt arbitrary 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, nor does the method of generating them need to be secret. A nonce could simply be a counter incremented with each message encrypted.

Both the sender and the receiver should record every nonce both that they’ve used and they’ve received from the other. They should reject any message which reuses a nonce and they should make absolutely sure never to reuse a nonce. It is not enough to simply use a random value and hope that it’s not being reused (simply generating random values would open up the system to a Birthday Attack).

One good method of generating nonces is for each person to pick a unique prefix, for example b"p1" and b"p2". When each person generates a nonce they prefix it, so instead of nacl.utils.random(24) you’d do b"p1" + nacl.utils.random(22). This prefix serves as a guarantee that no two messages from different people will inadvertently overlap nonces while in transit. They should still record every nonce they’ve personally used and every nonce they’ve received to prevent reuse or replays.


class nacl.secret.SecretBox(key, encoder=<class 'nacl.encoding.RawEncoder'>)[source]

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

The ciphertexts generated by 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.

  • key – The secret key used to encrypt and decrypt messages
  • encoder – The encoder class used to decode the given key
  • KEY_SIZE – The size that the key is required to be.
  • NONCE_SIZE – The size that the nonce is required to be.
decrypt(ciphertext, nonce=None, encoder=<class 'nacl.encoding.RawEncoder'>)[source]

Decrypts the ciphertext using the given nonce and returns the plaintext message.

  • ciphertext – [bytes] The encrypted message to decrypt
  • nonce – [bytes] The nonce used when encrypting the ciphertext
  • encoder – The encoder used to decode the ciphertext.
Return type:


encrypt(plaintext, nonce, encoder=<class 'nacl.encoding.RawEncoder'>)[source]

Encrypts the plaintext message using the given nonce and returns the ciphertext encoded with the encoder.


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.

  • plaintext – [bytes] The plaintext message to encrypt
  • nonce – [bytes] The nonce to use in the encryption
  • encoder – The encoder to use to encode the ciphertext
Return type:


class nacl.utils.EncryptedMessage[source]

A bytes subclass that holds a messaged that has been encrypted by a SecretBox.


The ciphertext contained within the EncryptedMessage.


The nonce used during the encryption of the EncryptedMessage.

Algorithm details

Encryption:Salsa20 steam cipher
Authentication:Poly1305 MAC

Table Of Contents

Previous topic

Public Key Encryption

Next topic

Digital Signatures

This Page