Thing is, this got me thinking - a nonce, in my book is a protection against forgery, but here I am giving it away.
The primary purpose of nonces is not protection against forgery, but rather, to ensure that encrypting the same plaintext many times with the same key does not produce the same ciphertext (which risks the possibility that an eavesdropper might learn something from frequency analysis). For a very simplistic example, imagine encrypting the plaintexts YES
and NO
over and over and they always encrypted to XUW
and EG
.
There are protocols that secondarily exploit nonces in some fashion to avoid replay or reordering attacks (a kind of forgery). For example, when encrypting a large volume of data, one strategy is to break it into smaller sequential chunks and encrypt each one separately with the same key but a nonce that encodes:
- The chunk number
- A flag indicating for each chunk whether it's the last one
...so that when you decrypt the file, you verify as you go that no chunks have been reordered and that it hasn't been truncated. But that's an optional thing an application may (wisely) choose to use chunks for, not a basic requisite like the ciphertext diversification.
I must say that I find the documentation that you link rather confusing in its treatment of nonces, because:
- For ciphers popular today (and used by the Libsodium C library this PHP package is seemingly based on), encrypting two textually different plaintexts with the same key and nonce can result in a catastrophic security loss;
- The linked page don't seem to sufficiently warn of the danger inherent to nonce reuse;
- It also doesn't explicitly specify over what scope the library expects nonces to be unique.
Compare what the Paragonie docs say:
Each message sent requires a nonce (a unique large number represented as a binary string that should only be used once).
...with its counterpart in the documentation of Libsodium library:
The nonce doesn't have to be confidential, but it should be used with just one invocation of crypto_box_easy()
for a particular pair of public and secret keys.
One easy way to generate a nonce is to use randombytes_buf()
, considering the size of the nonces the risk of any random collisions is negligible. For some applications, if you wish to use nonces to detect missing messages or to ignore replayed messages, it is also acceptable to use a simple incrementing counter as a nonce. A better alternative is to use the crypto_secretstream()
API.
When doing so you must ensure that the same value can never be re-used (for example you may have multiple threads or even hosts generating messages using the same key pairs).
Much, much better, and the recommendation to use random nonces is safe, but the alternative suggested of using a simple incrementing counter is a bit scary, it's much too easy to implement that wrong e.g. across multiple executions of the same program. They suggest the use of their newer crypto_secretstream()
API instead, but that uses shared-key encryption, not public key, leaving the user the highly non-obvious task of figuring out how to wire those together.
Caveat emptor. Cryptography is a minefield.