Monocypher

Boring crypto that simply works
CRYPTO_CHACHA20(3MONOCYPHER) 3MONOCYPHER CRYPTO_CHACHA20(3MONOCYPHER)

NAME

Chacha20 and XChacha20 encryption functions #include <monocypher.h>
void
crypto_chacha20(uint8_t *cipher_text, const uint8_t *plain_text, size_t text_size, const uint8_t key[32], const uint8_t nonce[8]);
void
crypto_xchacha20(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
crypto_chacha20_ctr(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);
uint64_t
crypto_xchacha20_ctr(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);

DESCRIPTION

These functions provide an interface for the Chacha20 encryption primitive.
Chacha20 is a low-level primitive. Consider using authenticated encryption, implemented by crypto_lock().
The arguments are:
key
A 32-byte secret key.
nonce
An 8-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 nonces cannot. 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 since the beginning of the stream.
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() performs a Chacha20 operation. It uses an 8-byte nonce, which is too small to be selected at random. Use a message counter as a nonce instead.
crypto_xchacha20() performs an XChacha20 operation. It uses a 24-byte nonce, which is large enough to be selected at random.
crypto_xchacha20() is recommended over crypto_chacha20(). The ability to use random nonces makes it easier to use securely, and the performance hit is often negligible in practice.
The crypto_chacha20() and crypto_xchacha20() 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.
The crypto_chacha20_ctr() and crypto_xchacha20_ctr() perform a Chacha20 or XChacha20 encryption, respectively, starting the stream at the block ctr (which is the byte ‘ctr × 64’). 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. Be careful when using this not to accidentally reuse parts of the random stream as that would destroy confidentiality.

RETURN VALUES

crypto_chacha20() and crypto_xchacha20() return nothing. crypto_chacha20_ctr() and crypto_xchacha20_ctr() functions return the next ctr to use with the same key and nonce values; this is always text_size divided by 64; plus one if there was a remainder.

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_xchacha20(cipher_text, plain_text, 500, key, nonce); 
/* 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       plain_text [500]; /* Message to decrypt       */ 
uint8_t       cipher_text[500]; /* Secret message           */ 
crypto_xchacha20(cipher_text, plain_text, 500, key, nonce); 
/* Wipe secrets if they are no longer needed */ 
crypto_wipe(key,        32); 
/* The plain text 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_xchacha20_ctr(cipher_text+i, plain_text+i, 64, 
                               key, nonce, ctr); 
} 
/* Process data that didn't fit into 64 byte pieces */ 
crypto_xchacha20_ctr(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 that, this is only meant to show how crypto_xchacha20_ctr() 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_xchacha20_ctr(cipher_text + (3 * 64), 
                     plain_text  + (3 * 64), 
                     500         - (3 * 64), 
                     key, nonce, 3); 
/* ...then encrypt the first part */ 
crypto_xchacha20_ctr(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);

SEE ALSO

crypto_ietf_chacha20(), crypto_lock(), crypto_wipe(), intro()

STANDARDS

These functions implement Chacha20 and XChacha20. 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_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.

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_lock().

Nonce reuse

Repeating a nonce with the same key exposes the XOR of two or more plain text messages, effectively destroying confidentiality.
For the same reason, do not select small nonces at random. The crypto_chacha20() 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 .. 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 plain text itself.
March 31, 2020 Linux 4.15.0-46-generic