CRYPTO_BLAKE2B(3MONOCYPHER)

πŸ“¦
version 4.0.3 Β· 2 versions
, in nixpkgs unstable

πŸ’– CRYPTO_BLAKE2B πŸ’–

okay so bestie, you're reading CRYPTO_BLAKE2B(3MONOCYPHER) β€” the local 3MONOCYPHER manual page β€” and it is giving cryptography main character energy. 🫢 Let's get into it.


πŸŽ€ NAME πŸŽ€

The whole BLAKE2b bestie squad: crypto_blake2b, crypto_blake2b_keyed, crypto_blake2b_init, crypto_blake2b_keyed_init, crypto_blake2b_update, and crypto_blake2b_final β€” doing hashing, message authentication, and key derivation with BLAKE2b. πŸ’…


✨ SYNOPSIS ✨

#include <monocypher.h>

void
crypto_blake2b(uint8_t *hash, size_t hash_size, const uint8_t *message,
    size_t message_size);

crypto_blake2b_keyed(uint8_t *hash, size_t hash_size, uint8_t *key,
    size_t key_size, const uint8_t *message, size_t message_size);

void
crypto_blake2b_init(crypto_blake2b_ctx *ctx, size_t hash_size);

void
crypto_blake2b_keyed_init(crypto_blake2b_ctx *ctx, size_t hash_size,
    uint8_t *key, size_t key_size);

void
crypto_blake2b_update(crypto_blake2b_ctx *ctx, const uint8_t *message,
    size_t message_size);

void
crypto_blake2b_final(crypto_blake2b_ctx *ctx, uint8_t *hash);

πŸ’… DESCRIPTION πŸ’…

🌸 Hashing 🌸

crypto_blake2b(), crypto_blake2b_init(), crypto_blake2b_update(), and crypto_blake2b_final() are the squad that implements BLAKE2b β€” a cryptographically secure hash based on the ideas of ChaCha20. It is faster than MD5, yet just as secure as SHA-3. ate and left no crumbs. ✨

🚨 real talk: BLAKE2b itself is not suitable for hashing passwords and deriving keys from them; use the crypto_argon2(3monocypher) family of functions for that purpose instead.

BLAKE2b is immune to length extension attacks, so it requires fewer precautions than older hashes. Still, we do recommend avoiding prefix-MAC and using keyed mode with crypto_blake2b_keyed() instead. Doing so enables better security arguments when using BLAKE2b as a random oracle. πŸ”₯

The arguments are:

  • config β€” the configuration parameter structure, see below.
  • hash β€” the output hash.
  • hash_size β€” length of hash, in bytes. Must be between 1 and 64. Anything below 32 is discouraged when using BLAKE2b as a general-purpose hash function.
  • message β€” the message to hash. May overlap with hash. May be NULL if message_size is 0.
  • message_size β€” length of message, in bytes.

πŸŽ€ Message authentication codes πŸŽ€

crypto_blake2b_keyed(), crypto_blake2b_keyed_init(), crypto_blake2b_update(), and crypto_blake2b_final() implement keyed BLAKE2b, and can be used for message authentication codes or as a random oracle, just like HMAC. 🫢

The arguments are:

  • hash β€” the output message authentication code (MAC). To avoid timing attacks, use crypto_verify16(3monocypher), crypto_verify32(3monocypher), or crypto_verify64(3monocypher) to compare (possibly truncated) MACs.
  • hash_size β€” length of hash, in bytes. Must be between 1 and 64. Anything below 16 is discouraged when using BLAKE2b as a message authentication code. Anything below 32 is discouraged when using BLAKE2b as a key derivation function (KDF).
  • key β€” some secret key. When uniformly random, one cannot predict the final hash without it. Users may want to wipe the key with crypto_wipe(3monocypher) once they are done with it. May overlap with hash or message. May be NULL if key_size is 0, in which case no key is used. Can also be used as a random salt in the context of KDF extraction, or as pre-shared-key in the context of KDF expansion.
  • key_size β€” length of key, in bytes. Must be between 0 and 64. 32 is a good default.
  • message β€” the message to compute the MAC for. May overlap with hash or key. May be NULL if message_size is 0. Can also be used as input key material in the context of KDF extraction, or to host a counter and domain separation string in the context of KDF expansion.
  • message_size β€” length of message, in bytes.

πŸ¦‹ Key derivation πŸ¦‹

BLAKE2b can be used to implement β€œkey derivation functions” (KDF) very similar to HKDF. The typical parameters of a KDF are:

  • IKM β€” input key material containing enough secret entropy to derive uniformly random keys from, such as the shared secret of a key exchange performed with crypto_x25519(3monocypher). Passwords do not contain enough entropy to be used as input key material. Hash them with crypto_argon2(3monocypher) instead.
  • salt β€” an optional random salt, used to increase the security of the output key material (OKM) in some settings. It should be either absent, constant, or contain at least 16 random bytes.
  • info β€” optional domain separation string for key derivation.

From these parameters, the KDF derives an arbitrary amount of independent random bytes, also called β€œoutput key material” (OKM), that can be used as regular encryption or secret keys. πŸ’œ

In practice, KDFs are often separated in two steps: β€œextraction” and β€œexpansion”. The extraction step reads the IKM (and optional salt) to derive a β€œpseudo-random key” (PRK), which can be used either directly as a regular key, or as an input for the extraction step. The expansion step then reads the PRK (and optional info) to make the OKM.

To implement KDF extraction, just call crypto_blake2b_keyed() with the message as the IKM and key as the salt, or crypto_blake2b() if there is no salt. This is unintuitive, but having the IKM in the message argument lets us input arbitrary amounts of data. It is also the HMAC and HKDF way. 🧚

For KDF expansion any pseudo-random-function (PRF) can be used. You can call crypto_blake2b_keyed() in counter mode with key as the PRK (though doing it manually is a bit tedious), or crypto_chacha20_djb(3monocypher) with its key as the PRK and its nonce for domain separation. 🌈

⭐ Incremental interface ⭐

An incremental interface is provided. It is useful for handling streams of data or large files without using too much memory. This interface uses three steps:

  • Initialisation with crypto_blake2b_init() or crypto_blake2b_keyed_init(), which set up a context with the hashing parameters;
  • Update with crypto_blake2b_update(), which hashes the message chunk by chunk and keeps the intermediary result in the context;
  • and Finalisation with crypto_blake2b_final(), which produces the final hash. The crypto_blake2b_ctx is automatically wiped upon finalisation.

Calling crypto_blake2b() is the same as calling crypto_blake2b_init(), crypto_blake2b_update(), and crypto_blake2b_final(). Calling crypto_blake2b_keyed() is the same as calling crypto_blake2b_keyed_init(), crypto_blake2b_update(), and crypto_blake2b_final().

Calling crypto_blake2b() is the same as calling crypto_blake2b_keyed() with an empty key. no cap. πŸ’…


🌟 RETURN VALUES 🌟

These functions return nothing. literally nothing. void vibes, zero drama, no return value. periodt. πŸ’€

✨ EXAMPLES ✨

Okay bestie, these examples all assume arc4random_buf() exists and that it fills your buffer with cryptographically secure random bytes. If arc4random_buf() is not a thing on your system, go peek at intro(3monocypher) β€” it has the tea on how to get cryptographically secure random bytes some other way. πŸ’–


🌸 Hashing a message all at once

uint8_t hash   [64]; /* Output hash (64 bytes)          */
uint8_t message[12] = "Lorem ipsum"; /* Message to hash */
crypto_blake2b(hash, sizeof(hash), message, sizeof(message));

One shot, no context, no drama. literally that's it. ✨


πŸ”₯ Computing a message authentication code all at once

uint8_t hash   [16];
uint8_t key    [32];
uint8_t message[11] = "Lorem ipsu"; /* Message to authenticate */
arc4random_buf(key, sizeof(key));
crypto_blake2b_keyed(hash   , sizeof(hash),
                     key    , sizeof(key),
                     message, sizeof(message));
/* Wipe secrets if they are no longer needed */
crypto_wipe(message, sizeof(message));
crypto_wipe(key,     sizeof(key));

Notice the 16-byte hash and 32-byte key β€” those are the sizes this example picked, and they're valid ranges, so we slay. πŸ’… And yes, wipe your secrets when you're done with them, bestie, that's just good hygiene. 🫢


πŸ¦‹ Hashing a message incrementally (without a key)

uint8_t hash   [ 64]; /* Output hash (64 bytes) */
uint8_t message[500] = {1}; /* Message to hash  */
crypto_blake2b_ctx ctx;
crypto_blake2b_init(&ctx, sizeof(hash));
for (size_t i = 0; i < 500; i += 100) {
        crypto_blake2b_update(&ctx, message + i, 100);
}
crypto_blake2b_final(&ctx, hash);

Chunk by chunk, 100 bytes at a time, all the way to 500. The vibe check here: init, then update as many times as you want, then final. Periodt. πŸ‘‘


πŸŽ€ Computing a message authentication code incrementally

uint8_t hash   [ 16];
uint8_t key    [ 32];
uint8_t message[500] = {1}; /* Message to authenticate */
arc4random_buf(key, sizeof(key));
crypto_blake2b_ctx ctx;
crypto_blake2b_keyed_init(&ctx, sizeof(hash), key,
                          sizeof(key));
/* Wipe the key */
crypto_wipe(key, sizeof(key));
for (size_t i = 0; i < 500; i += 100) {
        crypto_blake2b_update(&ctx, message + i, 100);
        /* Wipe secrets if they are no longer needed */
        crypto_wipe(message + i, 100);
}
crypto_blake2b_final(&ctx, hash);

Keyed version of the incremental life. Wipe the key right after crypto_blake2b_keyed_init() (it's done with it), then wipe each chunk of the message as you go if you don't need it anymore. main character energy is protecting your secrets. πŸ˜­πŸ’œ


🧚 Computing key derivation with BLAKE2b and ChaCha20

void kdf(uint8_t *okm,  size_t okm_size,  /* unlimited   */
         uint8_t *ikm,  size_t ikm_size,  /* unlimited   */
         uint8_t *salt, size_t salt_size, /* <= 64 bytes */
         uint8_t info[8])                 /* ==  8 bytes */
{
        /* Extract */
        uint8_t prk[32];
        crypto_blake2b_keyed(prk , sizeof(prk),
                             salt, salt_size,
                             ikm , ikm_size);
        /* Expand */
        crypto_chacha20_djb(okm, NULL, okm_size, prk, info, 0);
}

Bestie look at the size annotations, they're doing so much work here: okm_size and ikm_size are unlimited, salt_size is <= 64 bytes, and info is exactly 8 bytes for this one. "Extract" then "Expand", two steps, iconic. 🌈


⭐ Computing key derivation with BLAKE2b and XChaCha20

void xkdf(uint8_t *okm,  size_t okm_size,  /* unlimited   */
          uint8_t *ikm,  size_t ikm_size,  /* unlimited   */
          uint8_t *salt, size_t salt_size, /* <= 64 bytes */
          uint8_t *info, size_t info_size) /* unlimited   */
{
        /* Extract */
        uint8_t prk[32];
        crypto_blake2b_keyed(prk , sizeof(prk),
                             salt, salt_size,
                             ikm , ikm_size);
        /* Expand */
        uint8_t nonce[24];
        crypto_blake2b(nonce, sizeof(nonce), info, info_size);
        crypto_chacha20_x(okm, NULL, okm_size, prk, nonce, 0);
}

XChaCha20 version! Here info gets to be unlimited and info_size comes along for the ride, and the info bytes get hashed down into a 24-byte nonce with plain crypto_blake2b(). Same salt_size cap of <= 64 bytes. no cap, it's giving chef's kiss. πŸ’–


πŸ’€ Computing key derivation with BLAKE2b alone (a little tedious indeed)

#define MIN(a, b) ((a) < (b) ? (a) : (b))

void b2kdf(uint8_t *okm,  size_t okm_size,  /* unlimited   */
           uint8_t *ikm,  size_t ikm_size,  /* unlimited   */
           uint8_t *salt, size_t salt_size, /* <= 64 bytes */
           uint8_t *info, size_t info_size) /* unlimited   */
{
        /* Extract */
        uint8_t prk[64];
        crypto_blake2b_keyed(prk , sizeof(prk),
                             salt, salt_size,
                             ikm , ikm_size);
        /* Expand */
        uint8_t ctr[8] = {0};
        while(okm_size > 0) {
                size_t size = MIN(okm_size, 64);
                crypto_blake2b_ctx ctx;
                crypto_blake2b_keyed_init(&ctx, size, prk, 64);
                crypto_blake2b_update(&ctx, ctr, sizeof(ctr));
                crypto_blake2b_update(&ctx, info, info_size);
                crypto_blake2b_final(&ctx, okm);
                okm      += size;
                okm_size -= size;
                /* increment ctr */
                unsigned acc = 1;
                for (size_t i = 0; i < sizeof(ctr); i++) {
                        acc    = ctr[i] + acc;
                        ctr[i] = acc & 0xff;
                        acc  >>= 8;
                }
        }
}

No ChaCha20 to help you out, so it's BLAKE2b doing both jobs β€” hence the "a little tedious indeed" in the title, and honestly? fair. πŸ™ˆ Note the prk is 64 bytes here (bigger than the 32 in the ChaCha20 versions), the output gets churned out 64 bytes at a time via MIN(okm_size, 64), and there's a manual little-endian-ish ctr increment at the bottom. It uses crypto_blake2b_keyed_init(), crypto_blake2b_update(), and crypto_blake2b_final(). Functional, not glamorous. πŸ“


πŸ” SEE ALSO


πŸ“œ STANDARDS

These functions implement BLAKE2b, described in RFC 7693. That's the whole standards section, bestie, we love a concise moment. ✨


πŸ•°οΈ HISTORY

The crypto_blake2b(), crypto_blake2b_init(), crypto_blake2b_update(), and crypto_blake2b_final() functions first appeared in Monocypher 0.1.

Then in Monocypher 4.0.0 there was a whole glow-up/reshuffle:

  • crypto_blake2b_general() and crypto_blake2b_general_init() were removed πŸ’€
  • crypto_blake2b_keyed() and crypto_blake2b_keyed_init() were added πŸ‘‘
  • the hash_size argument was added to crypto_blake2b() and crypto_blake2b_init()

so if you're reading old code, that's why things look different. iykyk. 🌸


⚠️ 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. fr fr. 😭

Like, it will not hold your hand, it will not check your work, it will just do the thing with whatever you gave it. Double-check those sizes before you ship. πŸ™


πŸ” SECURITY CONSIDERATIONS

BLAKE2b is a general-purpose cryptographic hash function β€” and that means it is not suited for hashing passwords and deriving cryptographic keys from passwords. πŸ’…

Here's the reasoning, bestie: cryptographic keys usually have hundreds of bits of entropy, but passwords are often much less complex. When you're storing passwords as hashes, or deriving keys from them, the goal is normally to stop attackers from quickly iterating all possible passwords. And because passwords tend to be simple, you have to artificially slow attackers down by using computationally difficult hashing algorithms.

So Monocypher provides crypto_argon2(3monocypher) for password hashing and deriving keys from passwords. Use that one for passwords, not this one. periodt. 🫢


πŸ› οΈ IMPLEMENTATION DETAILS

The core loop is unrolled by default. This speeds up BLAKE2b by about 20% on modern processors. πŸ”₯

On the other hand, this inflates the binary size by several kilobytes, and is slower on some embedded platforms. To roll the loop and generate a smaller binary, compile Monocypher with the -DBLAKE2_NO_UNROLLING option. Trade-offs, babe! πŸŽ€


and that's the whole page! βœ¨πŸ’–πŸ¦‹ This one's from the Debian-flavoured Monocypher docs, dated February 2, 2023, and it's the CRYPTO_BLAKE2B(3MONOCYPHER) page β€” go hash responsibly, bestie, and don't forget to wipe your secrets. πŸ’œπŸ”πŸŒΈ

πŸ“¦ where this page comes from
  • nixpkgs unstable: monocypher 4.0.3
  • πŸ“œ the page's license: BSD-2-Clause OR CC0-1.0
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms