And optimised `crypto_blake2b_update()` on short or unaligned inputs.
This also clarifies the source code somewhat, though it's now a bit more
verbose. That verbosity does give us over 15-30% speed on small updates
& small inputs typical of key derivation schemes.
---
The reason for the rework of the API is that the struct argument simply
does not work in practice. See, BLAKE2b has not one, but *two* typical
usages: regular hashing, and keyed hashing. There are many situations
where controlling the size of the hash is useful even when we don't use
the key, and when we do keyed hashing (for MAC or KDF), having to use
the struct is just *so* verbose and tedious.
I briefly considered having just one hash function to rule them all with
6 arguments, but regular hashing still is the main use case. So instead
of a struct or the original split, I simply have:
* `crypto_blake2b()` with a variable sized hash.
* `crypto_blake2b_keyed()` with every option.
and in practice, the 6 arguments of the keyed version are quite
manageable: output first as always, then the key, then the message.
And if arguments get too long the pointer/size pairs give us natural
line breaks.
I had to work on KDFs to realise this so thanks @samuel-lucas6 for
bringing the whole issue up.
---
Unlike SHA512, I did *not* add an explicit KDF interface for BLAKE2b.
My reasons being:
* There is no clear standard KDF for BLAKE2b except HKDF.
* HKDF is build on top of HMAC, and HMAC is stupid when BLAKE2b already
has a keyed mode that does the exact same thing with less code and
fewer CPU cycles.
* HKDF is kind of *a little* stupid itself.
* `crypto_blake2b_keyed()` already implements KDF extract.
* `crypto_chacha20_*()` already implement KDF expand.
* I hesitate to implement a non-standard convenience function users are
unlikely to get catastrophically wrong.
So instead I updated the manual, including 3 full KDF functions lazy
users can just blindly copy & paste.
Closes #255
.\" with this software. If not, see
.\" <https://creativecommons.org/publicdomain/zero/1.0/>
.\"
-.Dd January 20, 2023
+.Dd February 02, 2023
.Dt CRYPTO_BLAKE2B 3MONOCYPHER
.Os
.Sh NAME
.Nm crypto_blake2b ,
+.Nm crypto_blake2b_keyed ,
.Nm crypto_blake2b_init ,
+.Nm crypto_blake2b_keyed_init ,
.Nm crypto_blake2b_update ,
.Nm crypto_blake2b_final
-.Nd cryptographic hashing
+.Nd hashing, message authentication, and key derivation with BLAKE2b
.Sh SYNOPSIS
.In monocypher.h
.Ft void
.Fo crypto_blake2b
.Fa "uint8_t hash[64]"
-.Fa "crypto_blake2b_config config"
+.Fa "size_t hash_size"
+.Fa "const uint8_t *message"
+.Fa "size_t message_size"
+.Fc
+.Fo crypto_blake2b_keyed
+.Fa "uint8_t hash[64]"
+.Fa "size_t hash_size"
+.Fa "uint8_t key[64]"
+.Fa "size_t key_size"
.Fa "const uint8_t *message"
.Fa "size_t message_size"
.Fc
.Ft void
.Fo crypto_blake2b_init
.Fa "crypto_blake2b_ctx *ctx"
-.Fa "crypto_blake2b_config config"
+.Fa "size_t hash_size"
+.Fc
+.Ft void
+.Fo crypto_blake2b_keyed_init
+.Fa "crypto_blake2b_ctx *ctx"
+.Fa "size_t hash_size"
+.Fa "uint8_t key[64]"
+.Fa "size_t key_size"
.Fc
.Ft void
.Fo crypto_blake2b_update
.Fa "crypto_blake2b_ctx *ctx"
.Fa "uint8_t *hash"
.Fc
-.Pp
-.Fa "extern const crypto_blake2b_config crypto_blake2b_defaults;"
.Sh DESCRIPTION
-BLAKE2b is a fast cryptographically secure hash based on the ideas of
-ChaCha20.
+.Ss Hashing
+.Fn crypto_blake2b ,
+.Fn crypto_blake2b_init ,
+.Fn crypto_blake2b_update ,
+and
+.Fn crypto_blake2b_final
+implement BLAKE2b,
+a cryptographically secure hash based on the ideas of ChaCha20.
It is faster than MD5, yet just as secure as SHA-3.
.Pp
Note that BLAKE2b itself is not suitable for hashing passwords and
.Xr crypto_argon2i 3monocypher
family of functions for that purpose instead.
.Pp
-BLAKE2b is immune to length extension attacks, and as such, does not
-require specific precautions such as using the HMAC algorithm.
+While BLAKE2b is immune to length extension attacks,
+and as such requires fewer precautions than older hashes,
+we do recommend avoiding prefix-MAC and using keyed mode with
+.Fn crypto_blake2b_keyed
+instead.
+Doing so enables better security arguments when using BLAKE2b as a
+random oracle.
.Pp
-The arguments to
-.Fn crypto_blake2b
-are:
+The arguments are:
.Bl -tag -width Ds
.It Fa config
The configuration parameter structure, see below.
.It Fa hash
The output hash.
+.It Fa hash_size
+Length of
+.Fa hash ,
+in bytes.
+Must be between 1 and 64.
+Anything below 32 is discouraged when using BLAKE2b as a general-purpose
+hash function.
.It Fa message
The message to hash.
May overlap with
.Fa message ,
in bytes.
.El
-.Pp
-The BLAKE2b hash function has configurable parameters to personalize
-instances.
-These parameters are given in a
-.Vt crypto_blake2b_config
-structure,
-which is defined as follows:
-.Bd -literal -offset Ds
-typedef struct {
- const uint8_t *key;
- size_t key_size;
- size_t hash_size;
-} crypto_blake2b_config;
-.Ed
-.Pp
-Its members are:
+.Ss Message authentication codes
+.Fn crypto_blake2b_keyed ,
+.Fn crypto_blake2b_keyed_init ,
+.Fn crypto_blake2b_update ,
+and
+.Fn 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:
.Bl -tag -width Ds
+.It Fa hash
+The output message authentication code (MAC).
+To avoid timing attacks,
+use
+.Xr crypto_verify16 3monocypher ,
+.Xr crypto_verify32 3monocypher ,
+or
+.Xr crypto_verify64 3monocypher
+to compare (possibly truncated) MACs.
.It Fa hash_size
Length of
.Fa hash ,
in bytes.
Must be between 1 and 64.
-Anything below 32 is discouraged when using BLAKE2b as a general-purpose
-hash function;
-anything below 16 is discouraged when using BLAKE2b as a message
+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).
.It Fa key
Some secret key.
-One cannot predict the final hash without it.
+When uniformly random,
+one cannot predict the final hash without it.
+Users may want to wipe the key with
+.Xr crypto_wipe 3monocypher
+once they are done with it.
+May overlap with
+.Fa hash or
+.Fa message .
May be
.Dv NULL
if
.Fa key_size
is 0, in which case no key is used.
-Keys can be used to create a message authentication code (MAC).
-Use
-.Xr crypto_verify16 3monocypher ,
-.Xr crypto_verify32 3monocypher ,
-or
-.Xr crypto_verify64 3monocypher
-to compare MACs created this way.
-Choose the size of the hash accordingly.
-Users may want to wipe the key with
-.Xr crypto_wipe 3monocypher
-once they are done with it.
+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.
.It Fa key_size
Length of
.Fa key ,
in bytes.
Must be between 0 and 64.
32 is a good default.
+.It Fa message
+The message to compute the MAC for.
+May overlap with
+.Fa hash or
+.Fa key .
+May be
+.Dv NULL
+if
+.Fa 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.
+.It Fa message_size
+Length of
+.Fa message ,
+in bytes.
+.El
+.Ss Key derivation
+BLAKE2b can be used to implement
+.Dq key derivation functions
+(KDF) very similar to HKDF.
+The typical parameters of a KDF are:
+.Bl -tag -width Ds
+.It Em 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
+.Xr crypto_x25519 3monocypher .
+Passwords do
+.Sy not
+contain enough entropy to be used as input key material.
+Hash them with
+.Xr crypto_argon2 3monocypher
+instead.
+.It Em 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.
+.It Em info
+Optional domain separation string for key derivation.
.El
.Pp
-The
-.Dv crypto_blake2b_defaults
-constant provides default
-.Fa config
-parameters:
-no key and a hash size of 64 bytes.
+From these parameters,
+the KDF derives an arbitrary amount of independent random bytes,
+also called
+.Dq output key material
+(OKM),
+that can be used as regular encryption or secret keys.
+.Pp
+In practice, KDFs are often separated in two steps:
+.Dq extraction
+and
+.Dq expansion .
+The extraction step reads the IKM (and optional salt) to derive a
+.Dq pseudo-random key
+(PRK),
+which can be used either directly as a regular key,
+.Em or
+as an input for the extraction step.
+The expansion step then reads the PRK (and optional info) to make the
+OKM.
.Pp
+To implement KDF extraction,
+just call
+.Fn crypto_blake2b_keyed
+with the
+.Fa message
+as the IKM and
+.Fa key
+as the salt,
+or
+.Fn crypto_blake2b
+if there is no salt.
+This is unintuitive,
+but having the IKM in the
+.Fa message
+argument lets us input arbitrary amounts of data.
+It is also the HMAC and HKDF way.
+.Pp
+For KDF expansion any pseudo-random-function (PRF) can be used.
+You can call
+.Fn crypto_blake2b_keyed
+in counter mode with
+.Fa key
+as the PRK
+(though doing it manually is a bit tedious),
+or
+.Xr crypto_chacha20_djb 3monocypher
+with its
+.Fa key
+as the PRK and its
+.Fa nonce
+for domain separation.
+.Ss Incremental interface
An incremental interface is provided.
It is useful for handling streams of data or
large files without using too much memory.
.Bl -bullet
.It
Initialisation with
-.Fn crypto_blake2b_init ,
-which sets up a context with the hashing parameters;
+.Fn crypto_blake2b_init
+or
+.Fn crypto_blake2b_keyed_init ,
+which set up a context with the hashing parameters;
.It
Update with
.Fn crypto_blake2b_update ,
is automatically wiped upon finalisation.
.El
.Pp
+Calling
.Fn crypto_blake2b
-is a convenience function that calls
+is the same as calling
.Fn crypto_blake2b_init ,
.Fn crypto_blake2b_update ,
and
-.Fn crypto_blake2b_final
-under the hood.
+.Fn crypto_blake2b_final .
+Calling
+.Fn crypto_blake2b_keyed
+is the same as calling
+.Fn crypto_blake2b_keyed_init ,
+.Fn crypto_blake2b_update ,
+and
+.Fn crypto_blake2b_final .
+.Pp
+Calling
+.Fn crypto_blake2b
+is the same as calling
+.Fn crypto_blake2b_keyed
+with an empty key.
.Sh RETURN VALUES
These functions return nothing.
.Sh EXAMPLES
.Bd -literal -offset indent
uint8_t hash [64]; /* Output hash (64 bytes) */
uint8_t message[12] = "Lorem ipsum"; /* Message to hash */
-crypto_blake2b(hash, crypto_blake2b_defaults, message, 12);
+crypto_blake2b(hash, sizeof(hash), message, sizeof(message));
.Ed
.Pp
Computing a message authentication code all at once:
uint8_t key [32];
uint8_t message[11] = "Lorem ipsu"; /* Message to authenticate */
arc4random_buf(key, sizeof(key));
-crypto_blake2b_config config = {
- .key = key,
- .key_size = sizeof(key),
- .hash_size = sizeof(hash),
-};
-crypto_blake2b(hash, config, message, sizeof(message));
+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));
uint8_t hash [ 64]; /* Output hash (64 bytes) */
uint8_t message[500] = {1}; /* Message to hash */
crypto_blake2b_ctx ctx;
-crypto_blake2b_init(&ctx, crypto_blake2b_defaults);
+crypto_blake2b_init(&ctx, sizeof(hash));
for (size_t i = 0; i < 500; i += 100) {
- crypto_blake2b_update(&ctx, message + i, 100);
+ crypto_blake2b_update(&ctx, message + i, 100);
}
crypto_blake2b_final(&ctx, hash);
.Ed
uint8_t message[500] = {1}; /* Message to authenticate */
arc4random_buf(key, sizeof(key));
crypto_blake2b_ctx ctx;
-crypto_blake2b_config config = {
- .key = key,
- .key_size = sizeof(key),
- .hash_size = sizeof(hash),
-};
-crypto_blake2b_init(&ctx, config);
+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_update(&ctx, message + i, 100);
+ /* Wipe secrets if they are no longer needed */
+ crypto_wipe(message + i, 100);
}
crypto_blake2b_final(&ctx, hash);
.Ed
+.Pp
+Computing key derivation with BLAKE2b and ChaCha20:
+.Bd -literal -offset indent
+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);
+}
+.Ed
+.Pp
+Computing key derivation with BLAKE2b and XChaCha20:
+.Bd -literal -offset indent
+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);
+}
+.Ed
+.Pp
+Computing key derivation with BLAKE2b alone
+(a little tedious indeed):
+.Bd -literal -offset indent
+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;
+ }
+ }
+}
+.Ed
.Sh SEE ALSO
.Xr crypto_x25519 3monocypher ,
.Xr crypto_lock 3monocypher ,
and
.Fn crypto_blake2b_general_init
were removed,
+.Fn crypto_blake2b_keyed
+and
+.Fn crypto_blake2b_keyed_init
+were added,
and the
-.Fa config
-argument was added to
-.Fn crypto_blake2b ,
-.Fn crypto_blake2b_init
-to compensate.
+.Fa hash_size
+argument were added to
+.Fn crypto_blake2b
+and
+.Fn crypto_blake2b_init .
.Sh CAVEATS
Monocypher does not perform any input validation.
Any deviation from the specified input and output length ranges results
and
.Fn crypto_sha512_hmac_final
implement HMAC with SHA-512,
-and can be used as for message authentication codes
-or as a random oracle.
+and can be used for message authentication codesor as a random oracle.
They are provided to enable compatibility with other cryptographic
systems.
It is generally recommended to use keyed
ctx->hash[6] ^= v6 ^ v14; ctx->hash[7] ^= v7 ^ v15;
}
-static void blake2b_set_input(crypto_blake2b_ctx *ctx, u8 input, size_t index)
-{
- if (index == 0) {
- ZERO(ctx->input, 16);
- }
- size_t word = index >> 3;
- size_t byte = index & 7;
- ctx->input[word] |= (u64)input << (byte << 3);
-}
-
-// Defaults are no key, 64 byte hash
-const crypto_blake2b_config crypto_blake2b_defaults = { 0, 0, 64 };
-
-void crypto_blake2b_init(crypto_blake2b_ctx *ctx, crypto_blake2b_config config)
+void crypto_blake2b_keyed_init(crypto_blake2b_ctx *ctx, size_t hash_size,
+ const u8 *key, size_t key_size)
{
// initial hash
COPY(ctx->hash, iv, 8);
- ctx->hash[0] ^= 0x01010000 ^ (config.key_size << 8) ^ config.hash_size;
+ ctx->hash[0] ^= 0x01010000 ^ (key_size << 8) ^ hash_size;
ctx->input_offset[0] = 0; // beginning of the input, no offset
ctx->input_offset[1] = 0; // beginning of the input, no offset
- ctx->hash_size = config.hash_size;
+ ctx->hash_size = hash_size;
ctx->input_idx = 0;
+ ZERO(ctx->input, 16);
// if there is a key, the first block is that key (padded with zeroes)
- if (config.key_size > 0) {
+ if (key_size > 0) {
u8 key_block[128] = {0};
- COPY(key_block, config.key, config.key_size);
+ COPY(key_block, key, key_size);
// same as calling crypto_blake2b_update(ctx, key_block , 128)
load64_le_buf(ctx->input, key_block, 16);
ctx->input_idx = 128;
}
}
+void crypto_blake2b_init(crypto_blake2b_ctx *ctx, size_t hash_size)
+{
+ crypto_blake2b_keyed_init(ctx, hash_size, 0, 0);
+}
+
void crypto_blake2b_update(crypto_blake2b_ctx *ctx,
const u8 *message, size_t message_size)
{
- // Align ourselves with block boundaries
- // The block that may result is not compressed yet
- size_t aligned = MIN(align(ctx->input_idx, 128), message_size);
- FOR (i, 0, aligned) {
- blake2b_set_input(ctx, *message, ctx->input_idx);
- ctx->input_idx++;
- message++;
- message_size--;
+ // Avoid UB pointer increments with empty messages
+ if (message_size == 0) {
+ return;
}
- // Process the message block by block
- // The last block is not compressed yet.
+ // Align with word boundaries
+ if ((ctx->input_idx & 7) != 0) {
+ size_t nb_bytes = MIN(align(ctx->input_idx, 8), message_size);
+ size_t word = ctx->input_idx >> 3;
+ size_t byte = ctx->input_idx & 7;
+ FOR (i, 0, nb_bytes) {
+ ctx->input[word] |= (u64)message[i] << ((byte + i) << 3);
+ }
+ ctx->input_idx += nb_bytes;
+ message += nb_bytes;
+ message_size -= nb_bytes;
+ }
+
+ // Align with block boundaries (faster than byte by byte)
+ if ((ctx->input_idx & 127) != 0) {
+ size_t nb_words = MIN(align(ctx->input_idx, 128), message_size) >> 3;
+ load64_le_buf(ctx->input + (ctx->input_idx >> 3), message, nb_words);
+ ctx->input_idx += nb_words << 3;
+ message += nb_words << 3;
+ message_size -= nb_words << 3;
+ }
+
+ // Process block by block
size_t nb_blocks = message_size >> 7;
FOR (i, 0, nb_blocks) {
if (ctx->input_idx == 128) {
}
message_size &= 127;
- // Fill remaining bytes (not the whole buffer)
- // The last block is never fully filled
- FOR (i, 0, message_size) {
+ if (message_size != 0) {
+ // Compress block & flush input buffer as needed
if (ctx->input_idx == 128) {
blake2b_compress(ctx, 0);
ctx->input_idx = 0;
}
- blake2b_set_input(ctx, message[i], ctx->input_idx);
- ctx->input_idx++;
+ if (ctx->input_idx == 0) {
+ ZERO(ctx->input, 16);
+ }
+ // Fill remaining words (faster than byte by byte)
+ size_t nb_words = message_size >> 3;
+ load64_le_buf(ctx->input + (ctx->input_idx >> 3), message, nb_words);
+ ctx->input_idx += nb_words << 3;
+ message += nb_words << 3;
+ message_size -= nb_words << 3;
+
+ // Fill remaining bytes
+ FOR (i, 0, message_size) {
+ size_t word = ctx->input_idx >> 3;
+ size_t byte = ctx->input_idx & 7;
+ ctx->input[word] |= (u64)message[i] << (byte << 3);
+ ctx->input_idx++;
+ }
}
}
void crypto_blake2b_final(crypto_blake2b_ctx *ctx, u8 *hash)
{
- // Pad the end of the block with zeroes
- FOR (i, ctx->input_idx, 128) {
- blake2b_set_input(ctx, 0, i);
- }
blake2b_compress(ctx, 1); // compress the last block
size_t hash_size = MIN(ctx->hash_size, 64);
size_t nb_words = hash_size >> 3;
FOR (i, nb_words << 3, hash_size) {
hash[i] = (ctx->hash[i >> 3] >> (8 * (i & 7))) & 0xff;
}
-
- // Expand the hash if it exceeds 64 bytes
- if (ctx->hash_size > 64) {
- u8 block[128] = {0};
- COPY(block + 16, hash, 64);
- store64_le(block + 8, (u64)(ctx->hash_size));
- u64 ctr = 0;
- while (ctx->hash_size > 0) {
- crypto_blake2b_config config = { 0, 0, MIN(ctx->hash_size, 64) };
- store64_le(block, ctr);
- crypto_blake2b(hash, config, block, 128);
- hash += config.hash_size;
- ctx->hash_size -= config.hash_size;
- ctr++;
- }
- }
WIPE_CTX(ctx);
}
-void crypto_blake2b(u8 *hash, crypto_blake2b_config config,
- const u8 *message, size_t message_size)
+void crypto_blake2b_keyed(u8 *hash, size_t hash_size,
+ const u8 *key, size_t key_size,
+ const u8 *message, size_t message_size)
{
crypto_blake2b_ctx ctx;
- crypto_blake2b_init (&ctx, config);
- crypto_blake2b_update(&ctx, message, message_size);
- crypto_blake2b_final (&ctx, hash);
+ crypto_blake2b_keyed_init(&ctx, hash_size, key, key_size);
+ crypto_blake2b_update (&ctx, message, message_size);
+ crypto_blake2b_final (&ctx, hash);
}
-void crypto_blake2_kdf(uint8_t *okm, size_t okm_size, // unlimited
- const uint8_t *ikm, size_t ikm_size, // unlimited
- const uint8_t *salt, size_t salt_size, // <= 64 bytes
- const uint8_t *info, size_t info_size) // unlimited
+void crypto_blake2b(u8 *hash, size_t hash_size, const u8 *msg, size_t msg_size)
{
- // Extract
- uint8_t prk[64];
- crypto_blake2b_config config = {
- .key = salt,
- .key_size = salt_size,
- .hash_size = 64,
- };
- crypto_blake2b(prk, config, ikm, ikm_size);
-
- // Expand
- uint64_t ctr = 0;
- while (okm_size > 0) {
- size_t hash_size = MIN(okm_size, 64);
- crypto_blake2b_config config = {
- .key = salt,
- .key_size = salt_size,
- .hash_size = hash_size,
- };
- uint8_t ctr_buf[8];
- store64_le(ctr_buf, ctr);
- crypto_blake2b_ctx ctx;
- crypto_blake2b_init (&ctx, config);
- crypto_blake2b_update(&ctx, info, info_size);
- crypto_blake2b_update(&ctx, ctr_buf, sizeof(ctr_buf));
- crypto_blake2b_final (&ctx, okm);
- okm += hash_size;
- okm_size -= hash_size;
- ctr++;
- }
+ crypto_blake2b_keyed(hash, hash_size, 0, 0, msg, msg_size);
}
-void crypto_blake2_kdf_expand_step(
- uint8_t *okm, size_t okm_size, // <= 64 bytes
- const uint8_t *prk, size_t prk_size, // <= 64 bytes
- const uint8_t *info, size_t info_size, // unlimited
- uint64_t ctr)
-{
- crypto_blake2b_config config = {
- .key = prk,
- .key_size = prk_size,
- .hash_size = okm_size,
- };
- uint8_t ctr_buf[8];
- store64_le(ctr_buf, ctr);
- crypto_blake2b_ctx ctx;
- crypto_blake2b_init (&ctx, config);
- crypto_blake2b_update(&ctx, info, info_size);
- crypto_blake2b_update(&ctx, ctr_buf, sizeof(ctr_buf));
- crypto_blake2b_final (&ctx, okm);
-}
-
-void crypto_blake2_kdf_expand_step2(
- uint8_t *okm, size_t okm_size, // <= 64 bytes
- const uint8_t *prk, size_t prk_size, // <= 64 bytes
- const uint8_t *info, size_t info_size, // <= 120 bytes
- uint64_t ctr)
-{
- crypto_blake2b_config config = {
- .key = prk,
- .key_size = prk_size,
- .hash_size = okm_size,
- };
- uint8_t block[128] = { 0 };
- store64_le(block, ctr);
- COPY(block + 8, info, info_size);
- crypto_blake2b(okm, config, block, sizeof(block));
-}
-
-void crypto_blake2_kdf2(uint8_t *okm, size_t okm_size, // unlimited
- const uint8_t *ikm, size_t ikm_size, // unlimited
- const uint8_t *salt, size_t salt_size, // <= 64 bytes
- const uint8_t *info, size_t info_size) // unlimited?
-{
- // Extract
- uint8_t prk[64];
- crypto_blake2b_config config = {
- .key = salt,
- .key_size = salt_size,
- .hash_size = 64,
- };
- crypto_blake2b(prk, config, ikm, ikm_size);
-
- // Expand
- uint64_t ctr = 0;
- while (okm_size > 0) {
- size_t hash_size = MIN(okm_size, 64);
- crypto_blake2_kdf_expand_step( // or step2
- okm, hash_size, prk, sizeof(prk), info, info_size, ctr);
- okm += hash_size;
- okm_size -= hash_size;
- ctr++;
- }
-}
-
-
-
//////////////
/// Argon2 ///
//////////////
static void extended_hash(u8 *digest, u32 digest_size,
const u8 *input , u32 input_size)
{
- crypto_blake2b_config config = { 0, 0, MIN(digest_size, 64) };
crypto_blake2b_ctx ctx;
- crypto_blake2b_init (&ctx, config);
+ crypto_blake2b_init (&ctx, MIN(digest_size, 64));
blake_update_32 (&ctx, digest_size);
crypto_blake2b_update(&ctx, input, input_size);
crypto_blake2b_final (&ctx, digest);
u32 out = 32;
while (i < r) {
// Input and output overlap. This is intentional
- crypto_blake2b(digest + out, crypto_blake2b_defaults,
- digest + in, 64);
+ crypto_blake2b(digest + out, 64, digest + in, 64);
i += 1;
in += 32;
out += 32;
}
- config.hash_size = digest_size - (32 * r);
- crypto_blake2b(digest + out, config, digest + in , 64);
+ crypto_blake2b(digest + out, digest_size - (32 * r), digest + in , 64);
}
}
{
u8 initial_hash[72]; // 64 bytes plus 2 words for future hashes
crypto_blake2b_ctx ctx;
- crypto_blake2b_init (&ctx, crypto_blake2b_defaults);
+ crypto_blake2b_init (&ctx, 64);
blake_update_32 (&ctx, config.nb_lanes ); // p: number of "threads"
blake_update_32 (&ctx, hash_size);
blake_update_32 (&ctx, config.nb_blocks);
COPY(a, seed, 32);
crypto_wipe(seed, 32);
COPY(secret_key, a, 32);
- crypto_blake2b(a, crypto_blake2b_defaults, a, 32);
+ crypto_blake2b(a, 64, a, 32);
crypto_eddsa_trim_scalar(a, a);
crypto_eddsa_scalarbase(secret_key + 32, a);
COPY(public_key, secret_key + 32, 32);
{
u8 hash[64];
crypto_blake2b_ctx ctx;
- crypto_blake2b_init (&ctx, crypto_blake2b_defaults);
+ crypto_blake2b_init (&ctx, 64);
crypto_blake2b_update(&ctx, a, a_size);
crypto_blake2b_update(&ctx, b, b_size);
crypto_blake2b_update(&ctx, c, c_size);
u8 h[32]; // publically verifiable hash of the message (not wiped)
u8 R[32]; // first half of the signature (allows overlapping inputs)
- crypto_blake2b(a, crypto_blake2b_defaults, secret_key, 32);
+ crypto_blake2b(a, 64, secret_key, 32);
crypto_eddsa_trim_scalar(a, a);
hash_reduce(r, a + 32, 32, message, message_size, 0, 0);
crypto_eddsa_scalarbase(R, r);
WIPE_BUFFER(t2);
}
-void crypto_x25519_to_eddsa(uint8_t eddsa[32], const uint8_t x25519[32])
+void crypto_x25519_to_eddsa(u8 eddsa[32], const u8 x25519[32])
{
// (x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))
// Only converting u to y, x is assumed positive.
// General purpose hash (BLAKE2b)
// ------------------------------
-typedef struct {
- const uint8_t *key;
- size_t key_size;
- size_t hash_size;
-} crypto_blake2b_config;
-
-extern const crypto_blake2b_config crypto_blake2b_defaults;
-
// Direct interface
-void crypto_blake2b(uint8_t *hash, crypto_blake2b_config config,
+void crypto_blake2b(uint8_t *hash, size_t hash_size,
const uint8_t *message, size_t message_size);
+void crypto_blake2b_keyed(uint8_t *hash, size_t hash_size,
+ const uint8_t *key, size_t key_size,
+ const uint8_t *message, size_t message_size);
+
// Incremental interface
typedef struct {
// Do not rely on the size or contents of this type,
size_t hash_size;
} crypto_blake2b_ctx;
-void crypto_blake2b_init(crypto_blake2b_ctx *ctx, crypto_blake2b_config config);
+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,
+ const 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);
RANDOM_INPUT(key, 32);
TIMING_START {
- crypto_blake2b_config config = {key, 32, 64};
- crypto_blake2b(hash, config, in, SIZE);
+ crypto_blake2b_keyed(hash, 64, key, 32, in, SIZE);
+ }
+ TIMING_END;
+}
+
+static u64 blake2b_small(void)
+{
+ u8 hash[64];
+ RANDOM_INPUT(input, 128*2);
+
+ TIMING_START {
+ FOR (i, 0, 128*2) {
+ crypto_blake2b_ctx ctx;
+ crypto_blake2b_init (&ctx, 64);
+ crypto_blake2b_update(&ctx, input , i);
+ crypto_blake2b_update(&ctx, input + i, 128*2 - i);
+ crypto_blake2b_final (&ctx, hash);
+ }
}
TIMING_END;
}
print("Poly1305 ",poly1305() *MUL ,"megabytes per second");
print("Auth'd encryption ",authenticated()*MUL ,"megabytes per second");
print("BLAKE2b ",blake2b() *MUL ,"megabytes per second");
+ print("BLAKE2b (small) ",blake2b_small() ,"cycles per second");
print("SHA-512 ",sha512() *MUL ,"megabytes per second");
print("Argon2i, 3 passes ",argon2i() *MUL ,"megabytes per second");
print("x25519 ",x25519() ,"exchanges per second");
print("EdDSA(check) ",edDSA_check() ,"checks per second");
print("x25519 inverse ",x25519_inverse() ,"scalar inv per second");
print("x25519 dirty fast ",x25519_sp_fast() ,"scalar inv per second");
- print("x25519 dirty small ",x25519_sp_small() ,"scalar inv per second");
+ print("x25519 dirty small ",x25519_sp_small() ,"scalar inv per second");
printf("\n");
return 0;
}
vector msg = next_input(reader);
vector key = next_input(reader);
vector out = next_output(reader);
- crypto_blake2b_config config = { key.buf, key.size, out.size, };
- crypto_blake2b(out.buf, config, msg.buf, msg.size);
+ crypto_blake2b_keyed(out.buf, out.size,
+ key.buf, key.size,
+ msg.buf, msg.size);
}
static void test_blake2b()
{
VECTORS(blake2b);
- printf("\tBLAKE2b (incremental)\n");
- // Note: I figured we didn't need to test keyed mode, or different
- // hash sizes, a second time. This test sticks to the simplified
- // interface.
-#undef INPUT_SIZE
-#define INPUT_SIZE (BLAKE2B_BLOCK_SIZE * 4 - 32) // total input size
- FOR (i, 0, INPUT_SIZE) {
- // outputs
+ printf("\tBLAKE2b (zero_input)\n");
+ {
+ RANDOM_INPUT(key, 32);
u8 hash_chunk[64];
u8 hash_whole[64];
- // inputs
- RANDOM_INPUT(input, INPUT_SIZE);
-
- // Authenticate bit by bit
crypto_blake2b_ctx ctx;
- crypto_blake2b_init(&ctx, crypto_blake2b_defaults);
- crypto_blake2b_update(&ctx, input , i);
- crypto_blake2b_update(&ctx, input + i, INPUT_SIZE - i);
- crypto_blake2b_final(&ctx, hash_chunk);
- // Authenticate all at once
- crypto_blake2b(hash_whole, crypto_blake2b_defaults, input, INPUT_SIZE);
+ // With key
+ memset(&ctx, 0x5c, sizeof(ctx));
+ crypto_blake2b_keyed_init(&ctx, 64, key, 32);
+ crypto_blake2b_final (&ctx, hash_chunk);
+ crypto_blake2b_keyed(hash_whole, 64, key, 32, 0, 0);
+ ASSERT_EQUAL(hash_chunk, hash_whole, 64);
- // Compare the results (must be the same)
+ // Without key
+ memset(&ctx, 0x5c, sizeof(ctx));
+ crypto_blake2b_init (&ctx, 64);
+ crypto_blake2b_final(&ctx, hash_chunk);
+ crypto_blake2b(hash_whole, 64, 0, 0);
ASSERT_EQUAL(hash_chunk, hash_whole, 64);
}
+ printf("\tBLAKE2b (incremental)\n");
+ // Note: I figured we didn't need to test keyed mode, or different
+ // hash sizes, a second time. This test sticks to the simplified
+ // interface.
+#undef INPUT_SIZE
+#define INPUT_SIZE (BLAKE2B_BLOCK_SIZE * 3) // total input size
+
+ RANDOM_INPUT(input, INPUT_SIZE);
+
+ // hash at once
+ u8 hash_whole[64];
+ crypto_blake2b(hash_whole, 64, input, INPUT_SIZE);
+
+ FOR (j, 0, INPUT_SIZE) {
+ FOR (i, 0, j+1) {
+ // Hash bit by bit
+ u8 *mid_input = j - i == 0 ? NULL : input + i; // test NULL update
+ u8 hash_chunk[64];
+ crypto_blake2b_ctx ctx;
+ crypto_blake2b_init (&ctx, 64);
+ crypto_blake2b_update(&ctx, input , i);
+ crypto_blake2b_update(&ctx, mid_input, j - i);
+ crypto_blake2b_update(&ctx, input + j, INPUT_SIZE - j);
+ crypto_blake2b_final (&ctx, hash_chunk);
+
+ // Compare the results (must be the same)
+ ASSERT_EQUAL(hash_chunk, hash_whole, 64);
+ }
+ }
+
printf("\tBLAKE2b (overlapping i/o)\n");
#undef INPUT_SIZE
#define INPUT_SIZE (BLAKE2B_BLOCK_SIZE + (2 * 64)) // total input size
FOR (i, 0, BLAKE2B_BLOCK_SIZE + 64) {
u8 hash [64];
RANDOM_INPUT(input, INPUT_SIZE);
- crypto_blake2b_config config = crypto_blake2b_defaults;
- crypto_blake2b(hash , config, input + 64, BLAKE2B_BLOCK_SIZE);
- crypto_blake2b(input+i, config, input + 64, BLAKE2B_BLOCK_SIZE);
+ crypto_blake2b(hash , 64, input + 64, BLAKE2B_BLOCK_SIZE);
+ crypto_blake2b(input+i, 64, input + 64, BLAKE2B_BLOCK_SIZE);
ASSERT_EQUAL(hash, input + i, 64);
}
}
{
printf("\tX25519 <-> EdDSA\n");
FOR (i, 0, 32) {
- crypto_blake2b_config config = crypto_blake2b_defaults;
RANDOM_INPUT(e_seed, 32);
u8 secret [64];
u8 e_public1[32]; crypto_eddsa_key_pair(secret, e_public1, e_seed);
- u8 x_private[64]; crypto_blake2b(x_private, config, secret, 32);
+ u8 x_private[64]; crypto_blake2b(x_private, 64, secret, 32);
u8 x_public1[32]; crypto_eddsa_to_x25519 (x_public1, e_public1);
u8 x_public2[32]; crypto_x25519_public_key(x_public2, x_private);
ASSERT_EQUAL(x_public1, x_public2, 32);
vector msg = next_input(reader);
vector key = next_input(reader);
vector out = next_output(reader);
- crypto_blake2b_config config = { key.buf, key.size, out.size, };
- crypto_blake2b(out.buf, config, msg.buf, msg.size);
+ crypto_blake2b_keyed(out.buf, out.size,
+ key.buf, key.size,
+ msg.buf, msg.size);
}
static void sha512(vector_reader *reader)
static int p_eddsa_x25519()
{
RANDOM_INPUT(e_seed, 32);
- crypto_blake2b_config config = crypto_blake2b_defaults;
u8 secret [64];
u8 e_public1[32]; crypto_eddsa_key_pair(secret, e_public1, e_seed);
- u8 x_private[64]; crypto_blake2b(x_private, config, secret, 32);
+ u8 x_private[64]; crypto_blake2b(x_private, 64, secret, 32);
u8 x_public1[32]; crypto_eddsa_to_x25519 (x_public1, e_public1);
u8 x_public2[32]; crypto_x25519_public_key(x_public2, x_private);
ASSERT_EQUAL(x_public1, x_public2, 32);