crypto_chacha20_encrypt,
crypto_chacha20_init,
crypto_chacha20_x_init,
crypto_chacha20_stream,
crypto_chacha20_set_ctr —
Chacha20 encryption
#include
<monocypher.h>
void
crypto_chacha20_init(
crypto_chacha_ctx
*ctx,
const uint8_t key[32],
const uint8_t nonce[8]);
void
crypto_chacha20_x_init(
crypto_chacha_ctx
*ctx,
const uint8_t key[32],
const uint8_t nonce[24]);
void
crypto_chacha20_encrypt(
crypto_chacha_ctx
*ctx,
uint8_t *cipher_text,
const uint8_t *plain_text,
size_t text_size);
void
crypto_chacha20_stream(
crypto_chacha_ctx
*ctx,
uint8_t *stream,
size_t stream_size);
void
crypto_chacha20_set_ctr(
crypto_chacha_ctx
*ctx,
uint64_t ctr);
These functions provide an incremental interface for the Chacha20 encryption
primitive.
Chacha20 is a low-level primitive. Consider using authenticated encryption,
implemented by
crypto_lock(3monocypher).
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(3monocypher) 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.
-
-
- stream
- The raw Chacha20 stream.
-
-
- stream_size
- Length of stream, 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_init() initialises the
crypto_chacha_ctx context. It uses an 8-byte
nonce, which is too small to be selected at random. Use a counter.
crypto_chacha20_x_init() initialises the
crypto_chacha_ctx context. It uses a 24-byte
nonce, which is big enough to be selected at random.
crypto_chacha20_x_init() is recommended over
crypto_chacha20_init(). The ability to use random
nonces makes it easier to use securely, and the performance hit is negligible
in practice.
The following functions need an initialised context to work properly. Calling
them with an uninitialised context triggers undefined behaviour.
crypto_chacha20_encrypt() encrypts
plain_text by XORing it with a pseudo-random
stream of numbers, seeded by the provided
crypto_chacha_ctx context. You may call
crypto_chacha20_encrypt() repeatedly with the
same context struct to encrypt a message incrementally. The
plain_text pointer is allowed to be
NULL
, in which case it will be interpreted
as an all zero input.
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 and context when you are done with encryption
or decryption. Use
crypto_wipe(3monocypher)
to wipe them.
crypto_chacha20_stream() is the same as
crypto_chacha20_encrypt() with
plain_text being
NULL
. This is useful as a user space random
number generator. While
this must not be used as a
cryptographic random number generator, it can be handy outside of a
security context. Deterministic procedural generation and reproducible
property-based tests come to mind.
crypto_chacha20_set_ctr() resets the internal
counter of the
crypto_chacha_ctx to the value
specified in
ctr. Resuming the encryption
will use 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 7539 (not implemented in
Monocypher because of its complexity and limitations). Be careful when using
this not to accidentally reuse parts of the random stream as that would
destroy confidentiality.
These functions return nothing. They cannot fail.
Simple encryption:
const uint8_t key [ 32]; /* Secret random key */
const uint8_t nonce [ 24]; /* Unique nonce (possibly random) */
const uint8_t plain_text [500]; /* Message to be encrypted */
uint8_t cipher_text[500]; /* Will be the encrypted message */
crypto_chacha_ctx ctx;
crypto_chacha20_x_init(&ctx, key, nonce);
crypto_chacha20_encrypt(&ctx, cipher_text, plain_text, 500);
/* Wipe secrets if they are no longer needed */
crypto_wipe(key, 32);
crypto_wipe(&ctx, sizeof(ctx));
crypto_wipe(plain_text, 500);
To decrypt the above:
const uint8_t key [ 32]; /* Same key as above */
const uint8_t nonce [ 24]; /* Same nonce as above */
const uint8_t cipher_text[500]; /* Encrypted message */
uint8_t plain_text [500]; /* Will be the decrypted message */
crypto_chacha_ctx ctx;
crypto_chacha20_x_init(&ctx, key, nonce);
crypto_chacha20_encrypt(&ctx, plain_text, cipher_text, 500);
/* Wipe secrets if they are no longer needed */
crypto_wipe(key, 32);
crypto_wipe(&ctx, sizeof(ctx));
/* The plain text likely needs to be processed before you wipe it */
crypto_wipe(plain_text, 500);
Incremental encryption:
const uint8_t key [ 32]; /* Secret random key */
const uint8_t nonce [ 24]; /* Unique nonce (possibly random) */
const uint8_t plain_text [500]; /* Message to be encrypted */
uint8_t cipher_text[500]; /* Will be the encrypted message */
crypto_chacha_ctx ctx;
crypto_chacha20_x_init(&ctx, key, nonce);
for(int i = 0; i < 500; i += 100) {
crypto_chacha20_encrypt(&ctx, cipher_text+i, plain_text+i, 100);
}
/* Wipe secrets if they are no longer needed */
crypto_wipe(key, 32);
crypto_wipe(&ctx, sizeof(ctx));
crypto_wipe(plain_text, 500);
Simple encryption with a small,
not random nonce:
const uint8_t key [ 32]; /* Secret, random key */
const uint8_t nonce [ 8]; /* Unique nonce (NOT random) */
const uint8_t plain_text [500]; /* Message to be encrypted */
uint8_t cipher_text[500]; /* Will be the encrypted message */
crypto_chacha_ctx ctx;
crypto_chacha20_init(&ctx, key, nonce);
crypto_chacha20_encrypt(&ctx, cipher_text, plain_text, 500);
/* Wipe secrets if they are no longer needed */
crypto_wipe(key, 32);
crypto_wipe(&ctx, sizeof(ctx));
crypto_wipe(plain_text, 500);
Encryption by jumping around (do not do that, this is only meant to show how
crypto_chacha20_set_ctr() works):
const uint8_t key [ 32]; /* Secret random key */
const uint8_t nonce [ 24]; /* Unique nonce (possibly random) */
const uint8_t plain_text [500]; /* Message to be encrypted */
uint8_t cipher_text[500]; /* Will be the encrypted message */
crypto_chacha_ctx ctx;
crypto_chacha20_x_init(&ctx, key, nonce);
/* Encrypt the second part of the message first... */
crypto_chacha20_set_ctr(&ctx, 3);
crypto_chacha20_encrypt(&ctx,
cipher_text + (3 * 64),
plain_text + (3 * 64),
500 - (3 * 64));
/* ...then encrypt the first part */
crypto_chacha20_set_ctr(&ctx, 0);
crypto_chacha20_encrypt(&ctx, cipher_text, plain_text, 3 * 64);
/* Wipe secrets if they are no longer needed */
crypto_wipe(key, 32);
crypto_wipe(&ctx, sizeof(ctx));
crypto_wipe(plain_text, 500);
crypto_lock(3monocypher),
crypto_wipe(3monocypher),
intro(3monocypher)
These functions implement Chacha20 and XChacha20. Chacha20 is described in RFC
7539. 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).
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(3monocypher)
and
crypto_lock(3monocypher)
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_init() nonce
spans only 64 bits, which is small enough to trigger accidental reuses. A
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.
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(3monocypher).
Secrets should not dwell in memory longer than needed. Use
crypto_wipe(3monocypher)
to erase secrets you no longer need. For Chacha20, this means the context, the
key, and in some cases the plain text itself.