CRYPTO_LOCK(3MONOCYPHER)

πŸ“¦ version 3.1.2, in fedora 43, fedora 44

πŸ’– CRYPTO_LOCK πŸ’–

okay bestie, welcome to the CRYPTO_LOCK(3MONOCYPHER) page β€” it's a LOCAL man page and it's literally the glow-up your secret messages deserve ✨


✨ NAME ✨

crypto_lock_aead, crypto_unlock_aead, crypto_lock, crypto_unlock β€” authenticated encryption with additional data

πŸŽ€ SYNOPSIS πŸŽ€

#include <monocypher.h>

void
crypto_lock(uint8_t mac[16], uint8_t *cipher_text,
    const uint8_t key[32], const uint8_t nonce[24],
    const uint8_t *plain_text, size_t text_size);

int
crypto_unlock(uint8_t *plain_text, const uint8_t key[32],
    const uint8_t nonce[24], const uint8_t mac[16],
    const uint8_t *cipher_text, size_t text_size);

void
crypto_lock_aead(uint8_t mac[16], uint8_t *cipher_text,
    const uint8_t key[32], const uint8_t nonce[24], const uint8_t *ad,
    size_t ad_size, const uint8_t *plain_text, size_t text_size);

int
crypto_unlock_aead(uint8_t *plain_text, const uint8_t key[32],
    const uint8_t nonce[24], const uint8_t mac[16], const uint8_t *ad,
    size_t ad_size, const uint8_t *cipher_text, size_t text_size);

🌸 DESCRIPTION 🌸

So crypto_lock() does two things at once, bestie: it encrypts your plaintext and authenticates it. That's the whole vibe. Then crypto_unlock() is the one who can decrypt it back. πŸ’…

Here's the cast of arguments, one by one:

  • key β€” a 32-byte session key that you and your recipient both share. It must be secret and random, no cap. There are different ways to make and swap this key: Diffie-Hellman key exchange, password key derivation (and if you go the password route, that password must be communicated on a secure channel), or honestly just meeting up in person, which is very main character of you. Check crypto_key_exchange(3monocypher) for key exchange, and crypto_argon2i(3monocypher) for password key derivation.
  • nonce β€” a 24-byte number, and you use it only once with any given session key. It doesn't have to be secret or random, it just has to be unique. Never use the same nonce twice with the same key. That would leak the XOR of 2 different messages, which is literally giving attackers decryption and forgeries, iykyk. The easiest and recommended way to make this nonce is to just pick it at random. Peep intro(3monocypher) about random number generation (use your operating system's random number generator, fr fr).
  • mac β€” a 16-byte message authentication code (MAC), and only someone who knows the session key can produce it. πŸ’– That guarantee cannot be upheld if a nonce has been reused with the session key, because reusing it lets the attacker learn the authentication key tied to that nonce. The MAC is meant to be sent along with the ciphertext.
  • plain_text β€” the secret message itself. Its contents stay hidden from attackers. Its length, though? Not hidden. πŸ™ˆ So be careful when you're combining encryption with compression. intro(3monocypher) has the details.
  • cipher_text β€” the encrypted message.
  • text_size β€” length of both plain_text and cipher_text, in bytes.

cipher_text and plain_text are allowed to point at the same buffer for in-place encryption, which is iconic. Otherwise, the buffers they point to must not overlap.

crypto_unlock() first checks the integrity of an encrypted message. If it's been corrupted, crypto_unlock() returns -1 immediately, no hesitation. Otherwise it decrypts the message and returns zero. Always check the return value. Always. 😀

crypto_lock_aead() and crypto_unlock_aead() are the variants of crypto_lock() and crypto_unlock() that let you add additional data. Additional data gets authenticated but not encrypted β€” it's for authenticating relevant data that can't be encrypted. The extra arguments:

  • ad β€” additional data to authenticate. It will not be encrypted. May be NULL if ad_size is zero. Setting ad_size to zero gives you the same results as crypto_lock() and crypto_unlock().
  • ad_size β€” length of the additional data, in bytes.

πŸ¦‹ RETURN VALUES πŸ¦‹

crypto_lock() and crypto_lock_aead() return nothing. They're above returning things. πŸ’…

crypto_unlock() and crypto_unlock_aead() return 0 on success, or -1 if the message was corrupted (i.e. mac mismatched the combination of key, nonce, ad and cipher_text). Corruption can come from transmission errors, programmer error, or an attacker's interference β€” all three are valid hater behaviors. Good news: plain_text does not need to be wiped if the decryption fails. 🫢

🌈 EXAMPLES 🌈

The following examples assume arc4random_buf() exists, which fills the given buffer with cryptographically secure random bytes. If arc4random_buf() does not exist on your system, see intro(3monocypher) for advice about how to generate cryptographically secure random bytes.

Encryption:

uint8_t key        [32];    /* Random, secret session key  */
uint8_t nonce      [24];    /* Use only once per key       */
uint8_t plain_text [12] = "Lorem ipsum"; /* Secret message */
uint8_t mac        [16];    /* Message authentication code */
uint8_t cipher_text[12];              /* Encrypted message */
arc4random_buf(key,   32);
arc4random_buf(nonce, 24);
crypto_lock(mac, cipher_text, key, nonce, plain_text,
        sizeof(plain_text));
/* Wipe secrets if they are no longer needed */
crypto_wipe(plain_text, 12);
crypto_wipe(key, 32);
/* Transmit cipher_text, nonce, and mac over the network,
 * store them in a file, etc.
 */

To decrypt the above:

uint8_t       key        [32]; /* Same as the above        */
uint8_t       nonce      [24]; /* Same as the above        */
const uint8_t cipher_text[12]; /* Encrypted message        */
const uint8_t mac        [16]; /* Received along with text */
uint8_t       plain_text [12]; /* Secret message           */
if (crypto_unlock(plain_text, key, nonce, mac, cipher_text, 12)) {
    /* The message is corrupted.
     * Wipe key if it is no longer needed,
     * and abort the decryption.
     */
    crypto_wipe(key, 32);
} else {
    /* ...do something with the decrypted text here... */
    /* Finally, wipe secrets if they are no longer needed */
    crypto_wipe(plain_text, 12);
    crypto_wipe(key, 32);
}

In-place encryption:

uint8_t key  [32];    /* Random, secret session key  */
uint8_t nonce[24];    /* Use only once per key       */
uint8_t text [12] = "Lorem ipsum"; /* Secret message */
uint8_t mac  [16];    /* Message authentication code */
arc4random_buf(key,   32);
arc4random_buf(nonce, 24);
crypto_lock(mac, text, key, nonce, text, 12);
/* Wipe secrets if they are no longer needed */
crypto_wipe(key, 32);
/* Transmit cipher_text, nonce, and mac over the network,
 * store them in a file, etc.
 */

In-place decryption:

uint8_t        key  [32]; /* Same as the above             */
const uint8_t  nonce[24]; /* Same as the above             */
const uint8_t  mac  [16]; /* Received from along with text */
uint8_t        text [12]; /* Message to decrypt            */
if (crypto_unlock(text, key, nonce, mac, text, 12)) {
    /* The message is corrupted.
     * Wipe key if it is no longer needed,
     * and abort the decryption.
     */
    crypto_wipe(key, 32);
} else {
    /* ...do something with the decrypted text here... */
    /* Finally, wipe secrets if they are no longer needed */
    crypto_wipe(text, 12);
    crypto_wipe(key, 32);
}

πŸ‘‘ SEE ALSO πŸ‘‘

crypto_key_exchange(3monocypher), crypto_wipe(3monocypher), intro(3monocypher)

⭐ STANDARDS ⭐

These functions implement RFC 8439, but with XChacha20 instead of Chacha20. XChacha20 derives from Chacha20 the same way XSalsa20 derives from Salsa20, and it benefits from the same security reduction (proven secure as long as Chacha20 itself is secure). Slay. πŸ’…

🧚 HISTORY 🧚

The crypto_lock() and crypto_unlock() functions first appeared in Monocypher 0.1. Then crypto_lock_aead() and crypto_unlock_aead() were introduced in Monocypher 1.1.0. In Monocypher 2.0.0, the underlying algorithms for these functions were changed from a custom XChacha20/Poly1305 construction to an implementation of RFC 7539 (now RFC 8439) with XChacha20 instead of Chacha20. Also in Monocypher 2.0.0, the crypto_lock_encrypt() and crypto_lock_auth() functions were removed. gone, deleted, not me crying about it 😭


and that's the whole page bestie!! this one came from the Debian peeps, dated February 29, 2020 β€” a leap day drop, which is honestly so iconic of them. now go check your return values. πŸ’–βœ¨πŸ¦‹

πŸ“¦ where this page comes from