I was advised to use crypto_secretbox_xsalsa20poly1305 due to its simplicity and because it's safe against timing attacks. I'm reading the secretbox tutorial, and there's a link to Validation and Verification regarding message length. In that, in the last paragraph, it says the following:

Tests are currently limited to 4096-byte messages. This is one of several reasons that callers should (1) split all data into packets sent through the network; (2) put a small global limit on packet length; and (3) separately encrypt and authenticate each packet.

Now I don't understand who's the "caller" here. Is it me in my application or the NaCl library? From requirement (1) it seems it's me. From requirement (3) it seems it's NaCl, because I don't have control on separating encryption and implementation, because both happen in one call of crypto_secretbox_xsalsa20poly1305.

So I guess my question is: If I have a message whose length is longer than 4096 bytes, should I manually split it when using crypto_secretbox_xsalsa20poly1305, encrypt and authenticate each, then combine them together in a certain way as my final cipher?


This is advice to you, the application protocol designer, on how to use the cryptography. The word ‘separately’ applies to the packets, not to encrypt and authenticate: use crypto_secretbox_xsalsa20poly1305 for one ≤4096-byte packet at a time.

You should split longer messages into ≤4096-byte packets with sequence numbers. This way:

  1. The most memory an adversary can force the receiver to waste at a time by sending forgeries is 4096 bytes.
  2. The least bandwidth that the sender needs in order to make progress during a denial of service attack is 4096 bytes.
  • $\begingroup$ I don't know whether this needs a new question, but does this also apply to the _easy variants of these functions? I'm having really big trouble finding any conclusive documentation. $\endgroup$ – The Quantum Physicist Apr 23 '19 at 18:14
  • $\begingroup$ @TheQuantumPhysicist The only difference in the _easy variants is that you don't have to provide space for zero-padding in the buffers you pass in. That is: with the _easy variants, you pass a pointer to a message and pointer to a ciphertext, period, not a pointer to a handful of zero bytes preceding a message, etc. $\endgroup$ – Squeamish Ossifrage Apr 23 '19 at 18:31
  • $\begingroup$ Got it. Thanks. $\endgroup$ – The Quantum Physicist Apr 23 '19 at 18:42

Are you actually sending these messages over the network?

If you don't, this limit doesn't apply, even though splitting large messages into chunks is still worth it: the whole message doesn't have to fit in memory, and an invalid message can be rejected earlier.

If you do, 4 KiB is a very conservative size, that you can safely bump up on most modern networks and devices. TLS typically uses 16 KiB chunks.

Once messages are split, you need to make sure that they are received exactly as they were sent, without any deletion, truncation, duplication, reordering, or replay from previous streams.

If you are using libsodium, you can use the dedicated secretstream API to achieve this.

If you really want to write your own protocol, the same documentation includes some guidance to encrypt a sequence of messages.

  • $\begingroup$ Actually this is sent over network, but it's used to encrypt session information. It's not encryption of a stream. Thanks for the info though, very valuable. $\endgroup$ – The Quantum Physicist Apr 24 '19 at 1:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.