CRYPTO_CHACHA20(3MONOCYPHER) | 3MONOCYPHER | CRYPTO_CHACHA20(3MONOCYPHER) |
NAME
and XChaCha20 encryption functionsSYNOPSIS
#include
<monocypher.h>
uint64_t
crypto_chacha20_djb
(uint8_t
*cipher_text, const uint8_t *plain_text,
size_t text_size, const uint8_t
key[32], const uint8_t nonce[8],
uint64_t ctr);
uint32_t
crypto_chacha20_ietf
(uint8_t
*cipher_text, const uint8_t *plain_text,
size_t text_size, const uint8_t
key[32], const uint8_t nonce[12],
uint32_t ctr);
uint64_t
crypto_chacha20_x
(uint8_t
*cipher_text, const uint8_t *plain_text,
size_t text_size, const uint8_t
key[32], const uint8_t nonce[24],
uint64_t ctr);
void
crypto_chacha20_h
(uint8_t
out[32], const uint8_t key[32],
const uint8_t in[16]);
DESCRIPTION
These functions provide an interface for the ChaCha20 encryption primitive.
ChaCha20 is a low-level primitive. Consider using authenticated encryption, implemented by crypto_aead_lock().
The arguments are:
- key
- A 32-byte secret key.
- nonce
- An 8-byte, 12-byte, or 24-byte number used only once with any given key. It does not need to be secret or random, but it does have to be unique. Repeating a nonce with the same key reveals the XOR of two different messages, which allows decryption. 24-byte nonces can be selected at random. 8-byte and 12-byte nonces cannot because they are too small and the same nonce may be selected twice by accident. See intro() for advice about generating random numbers (use the operating system's random number generator).
- plain_text
- The message to encrypt. It is allowed to be
NULL
, in which case it will be interpreted as an all-zero input. cipher_text will then contain the raw ChaCha20 stream. - cipher_text
- The encrypted message.
- text_size
- Length of both plain_text and cipher_text, in bytes.
- ctr
- The number of 64-byte blocks we skip from the beginning of the stream. This can be used to encrypt (or decrypt) part of a long message or to implement some AEAD constructions such as the one described in RFC 8439. Should be zero by default. When using this, be careful not to accidentally reuse parts of the random stream as that would destroy confidentiality. The return value can help here.
The key and nonce buffers may overlap. plain_text and cipher_text must either be the same buffer (for in-place encryption) or non-overlapping.
crypto_chacha20_djb
(),
crypto_chacha20_ietf
(),
and crypto_chacha20_x
() perform a ChaCha20
operation. Their main difference is the size of their nonce and counter.
crypto_chacha20_ietf
() in particular implements RFC
8439, and is provided strictly for compatibility with existing systems or
standards compliance.
crypto_chacha20_x
()
Is the only function that uses a nonce long enough to be random. This makes
it easier to use securely, and the performance hit of the extended nonce is
often negligible in practice. Use it instead of
crypto_chacha20_djb
() and
crypto_chacha20_ietf
() if possible.
The
crypto_chacha20_djb
(),
crypto_chacha20_ietf
(),
and crypto_chacha20_x
() encrypt
plain_text by XORing it with a pseudo-random stream of
numbers, seeded by the provided key and
nonce.
Since XOR is its own inverse, decryption is the same operation as encryption. To decrypt the cipher text, “encrypt” it again with the same key and nonce. You will likely want to wipe the key when you are done with encryption or decryption. Use crypto_wipe() to wipe them.
The plain_text pointer is allowed
to be NULL
, in which case it will be interpreted as
an all-zero input. This is useful as a user space random number generator.
While this should not be
used as a random number generator for secrets, for which the
operating system random number generator should be preferred, it can be
handy outside of a security context. Deterministic procedural generation and
reproducible property-based tests come to mind. Additionally, it
can be used
to generate large amounts of random-looking data quickly – for
example to generate padding.
RETURN VALUES
crypto_chacha20_djb
(),
crypto_chacha20_ietf
(), and
crypto_chacha20_x
() return the next
ctr to use with the same key and nonce values; this is
the previous ctr, plus text_size
divided by 64 (rounded up).
EXAMPLES
The following examples assume the existence of
arc4random_buf
(), which fills the given buffer with
cryptographically secure random bytes. If
arc4random_buf
() does not exist on your system, see
intro() for advice about how to
generate cryptographically secure random bytes.
Simple encryption:
uint8_t key [ 32]; /* Secret random key */ uint8_t nonce [ 24]; /* Unique nonce (possibly random) */ uint8_t plain_text [500] = {1}; /* Secret message */ uint8_t cipher_text[500]; /* Encrypted message */ arc4random_buf(key, 32); arc4random_buf(nonce, 24); crypto_chacha20_x(cipher_text, plain_text, 500, key, nonce, 0); /* Wipe secrets if they are no longer needed */ crypto_wipe(key, 32); crypto_wipe(plain_text, 500);
To decrypt the above:
uint8_t key [ 32]; /* Same key as above */ const uint8_t nonce [ 24]; /* Same nonce as above */ uint8_t cipher_text[500]; /* Message to decrypt */ uint8_t plain_text [500]; /* Secret message */ crypto_chacha20_x(plain_text, cipher_text, 500, key, nonce, 0); /* Wipe secrets if they are no longer needed */ crypto_wipe(key, 32); /* The plaintext likely needs to be processed before you wipe it */ crypto_wipe(plain_text, 12);
Incremental encryption (in blocks of 64 bytes):
uint8_t key [ 32]; /* Secret random key */ uint8_t nonce [ 24]; /* Unique nonce (possibly random) */ uint8_t plain_text [500]; /* Secret message */ uint8_t cipher_text[500]; /* Encrypted message */ uint64_t ctr = 0; /* Block counter */ unsigned int i; arc4random_buf(key, 32); arc4random_buf(nonce, 24); for(i = 0; i < 500; i += 64) { ctr = crypto_chacha20_x(cipher_text+i, plain_text+i, 64, key, nonce, ctr); } /* Process data that didn't fit into 64-byte pieces */ crypto_chacha20_x(cipher_text+500-(i-64), plain_text+500-(i-64), 500-(i-64), key, nonce, ctr); /* Wipe secrets if they are no longer needed */ crypto_wipe(key, 32); crypto_wipe(plain_text, 500);
Encryption by jumping around (do not do this, this is only meant to show how the counter works):
uint8_t key [ 32]; /* Secret random key */ uint8_t nonce [ 24]; /* Unique nonce (possibly random) */ uint8_t plain_text [500] = {1}; /* Message to be encrypted */ uint8_t cipher_text[500]; /* Will be the encrypted message */ arc4random_buf(key, 32); arc4random_buf(nonce, 24); /* Encrypt the second part of the message first... */ crypto_chacha20_x(cipher_text + (3 * 64), plain_text + (3 * 64), 500 - (3 * 64), key, nonce, 3); /* ...then encrypt the first part */ crypto_chacha20_x(cipher_text, plain_text, 3 * 64, key, nonce, 0); /* Wipe secrets if they are no longer needed */ crypto_wipe(key, 32); crypto_wipe(plain_text, 500);
HCHACHA20
crypto_chacha20_h
()
provides a not-so-cryptographic hash.
This is not a
general-purpose cryptographic hash function. It may be used for some
specific purposes such as X25519 key derivation or XChaCha20 initialisation.
If in doubt, do not use directly. Use
crypto_blake2b()
instead.
The arguments are:
- key
- A sufficiently random key, such as the output of crypto_x25519().
- in
- The space reserved for the ChaCha20 nonce and counter. It does not have to be random.
- out
- A cryptographically secure random number if there is enough entropy in key. X25519 shared secrets have enough entropy.
For instance:
uint8_t key[32]; /* Must have enough entropy */ uint8_t in [16]; /* Does not have to be random */ uint8_t out[32]; /* Will be random iff the above holds */ arc4random_buf(key, 32); crypto_chacha20_h(out, key, in); /* Wipe secrets if they are no longer needed */ crypto_wipe(key, 32); crypto_wipe(in , 16);
SEE ALSO
STANDARDS
These functions implement ChaCha20, XChaCha20, and HChaCha20. ChaCha20 is described in: Daniel J. Bernstein, ChaCha, a variant of Salsa20, SASC 2008 – The State of the Art of Stream Ciphers, pp. 273–278. The nonce and counter sizes were modified in RFC 8439. XChaCha20 derives from ChaCha20 the same way XSalsa20 derives from Salsa20 and benefits from the same security reduction (proven secure as long as ChaCha20 itself is secure).
HISTORY
crypto_chacha20
(),
crypto_chacha20_ctr
(),
crypto_ietf_chacha20
(),
crypto_ietf_chacha20_ctr
(),
crypto_xchacha20
(), and
crypto_xchacha20_ctr
() were added in Monocypher
3.0.0. They replace crypto_chacha20_encrypt
(),
crypto_chacha20_init
(),
crypto_chacha20_stream
(),
crypto_chacha20_x_init
(), and
crypto_chacha20_set_ctr
() that were deprecated in
Monocypher 3.0.0. In Monocypher 4.0.0, only the ctr variants have been kept,
and were renamed crypto_chacha20_djb
(),
crypto_chacha20_ietf
(), and
crypto_chacha20_x
() respectively.
crypto_chacha20_h
() function first
appeared in Monocypher 0.1 as crypto_chacha20_H
().
It was renamed to crypto_hchacha20
() in Monocypher
3.0.0, then crypto_chacha20_h
() in Monocypher
4.0.0.
CAVEATS
Monocypher does not perform any input validation. Any deviation from the specified input and output length ranges results in undefined behaviour. Make sure your inputs are correct.
SECURITY CONSIDERATIONS
Encrypted does not mean secure
ChaCha20 only protects against eavesdropping, not forgeries. Most applications need protection against forgeries to be properly secure. To ensure the integrity of a message, use BLAKE2b in keyed mode or authenticated encryption; see crypto_blake2b() and crypto_aead_lock().
Nonce reuse
Repeating a nonce with the same key exposes the XOR of two or more plaintext messages, effectively destroying confidentiality.
For the same reason,
do not select small nonces at
random. The crypto_chacha20_djb
() nonce
spans only 64 bits, which is small enough to trigger accidental reuses. A
message counter should be used instead. If multiple parties send out
messages, each can start with an initial nonce of 0, 1, 2 (...) n-1
respectively, and increment them by n for each new message. Make sure the
counters never wrap around.
Secure random number generation
Do not use these functions as a cryptographic random number generator. Always use the operating system's random number generator for cryptographic purposes; see intro().
Protection against side channels
Secrets should not dwell in memory longer than needed. Use crypto_wipe() to erase secrets you no longer need. For ChaCha20, this means the key and in some cases the plaintext itself.
February 25, 2023 | Debian |