From: Loup Vaillant Date: Thu, 2 Feb 2023 23:14:15 +0000 (+0100) Subject: Document BLAKE2b KDF, change BLAKE2b API *again* X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=531000da9204f229d586c1a9cdf558d03a195d53;p=Monocypher.git Document BLAKE2b KDF, change BLAKE2b API *again* 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 --- diff --git a/doc/crypto_blake2b.3monocypher b/doc/crypto_blake2b.3monocypher index fc8c8be..9c66b61 100644 --- a/doc/crypto_blake2b.3monocypher +++ b/doc/crypto_blake2b.3monocypher @@ -50,28 +50,45 @@ .\" with this software. If not, see .\" .\" -.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 @@ -84,11 +101,15 @@ .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 @@ -97,17 +118,27 @@ use the .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 @@ -122,66 +153,156 @@ Length of .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. @@ -189,8 +310,10 @@ This interface uses three steps: .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 , @@ -205,13 +328,26 @@ The 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 @@ -228,7 +364,7 @@ Hashing a message all at once: .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: @@ -237,12 +373,9 @@ uint8_t hash [16]; 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)); @@ -253,9 +386,9 @@ 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, 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 @@ -267,21 +400,88 @@ uint8_t key [ 32]; 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 , @@ -301,12 +501,16 @@ In Monocypher 4.0.0, 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 diff --git a/doc/crypto_sha512.3monocypher b/doc/crypto_sha512.3monocypher index 62ba438..a9a1365 100644 --- a/doc/crypto_sha512.3monocypher +++ b/doc/crypto_sha512.3monocypher @@ -187,8 +187,7 @@ in bytes. 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 diff --git a/src/monocypher.c b/src/monocypher.c index 99306f5..7e05871 100644 --- a/src/monocypher.c +++ b/src/monocypher.c @@ -538,55 +538,65 @@ static void blake2b_compress(crypto_blake2b_ctx *ctx, int is_last_block) 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) { @@ -598,24 +608,34 @@ void crypto_blake2b_update(crypto_blake2b_ctx *ctx, } 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; @@ -623,135 +643,24 @@ void crypto_blake2b_final(crypto_blake2b_ctx *ctx, u8 *hash) 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 /// ////////////// @@ -788,9 +697,8 @@ static void xor_block(blk *o,const blk*in){FOR(i, 0, 128) o->a[i] ^= in->a[i];} 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); @@ -804,14 +712,12 @@ static void extended_hash(u8 *digest, u32 digest_size, 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); } } @@ -863,7 +769,7 @@ void crypto_argon2(u8 *hash, u32 hash_size, void *work_area, { 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); @@ -2320,7 +2226,7 @@ void crypto_eddsa_key_pair(u8 secret_key[64], u8 public_key[32], u8 seed[32]) 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); @@ -2334,7 +2240,7 @@ static void hash_reduce(u8 h[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); @@ -2405,7 +2311,7 @@ void crypto_eddsa_sign(u8 signature [64], const u8 secret_key[64], 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); @@ -2451,7 +2357,7 @@ void crypto_eddsa_to_x25519(u8 x25519[32], const u8 eddsa[32]) 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. diff --git a/src/monocypher.h b/src/monocypher.h index adeaafa..c52c8ff 100644 --- a/src/monocypher.h +++ b/src/monocypher.h @@ -122,18 +122,14 @@ int crypto_aead_read(crypto_aead_ctx *ctx, // 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, @@ -145,7 +141,9 @@ typedef struct { 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); diff --git a/tests/speed/speed.c b/tests/speed/speed.c index dfa0fb8..8dd7937 100644 --- a/tests/speed/speed.c +++ b/tests/speed/speed.c @@ -100,8 +100,24 @@ static u64 blake2b(void) 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; } @@ -222,6 +238,7 @@ int main() 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"); @@ -229,7 +246,7 @@ int main() 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; } diff --git a/tests/test.c b/tests/test.c index 492f9fa..cd871af 100644 --- a/tests/test.c +++ b/tests/test.c @@ -400,50 +400,75 @@ static void blake2b(vector_reader *reader) 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); } } @@ -1107,11 +1132,10 @@ static void test_conversions() { 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); diff --git a/tests/tis-ci.c b/tests/tis-ci.c index 5798f70..c4e9ca7 100644 --- a/tests/tis-ci.c +++ b/tests/tis-ci.c @@ -138,8 +138,9 @@ static void blake2b(vector_reader *reader) 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) @@ -266,10 +267,9 @@ static void elligator_inv(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);