From: Niels Möller Date: Thu, 13 Nov 2025 20:53:52 +0000 (+0100) Subject: Implement blake2b and blake2s. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=35d6689b5c97ba847318b4cf35e679c1ad5e6d31;p=thirdparty%2Fnettle.git Implement blake2b and blake2s. --- diff --git a/Makefile.in b/Makefile.in index c08e589a..0a106cd9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -97,6 +97,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt-table.c \ base16-encode.c base16-decode.c \ base64-encode.c base64-decode.c \ base64url-encode.c base64url-decode.c \ + blake2b.c blake2s.c \ buffer.c buffer-init.c \ camellia-crypt-internal.c camellia-table.c \ camellia-absorb.c camellia-invert-key.c \ @@ -241,7 +242,7 @@ hogweed_SOURCES = sexp.c sexp-format.c \ OPT_SOURCES = fat-arm.c fat-arm64.c fat-ppc.c fat-s390x.c fat-x86_64.c mini-gmp.c HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h balloon.h \ - base16.h base64.h bignum.h buffer.h camellia.h cast128.h \ + base16.h base64.h bignum.h blake2.h buffer.h camellia.h cast128.h \ cbc.h ccm.h cfb.h chacha.h chacha-poly1305.h ctr.h \ curve25519.h curve448.h des.h dsa.h eax.h \ ecc-curve.h ecc.h ecdsa.h eddsa.h \ diff --git a/blake2.h b/blake2.h new file mode 100644 index 00000000..8741f5a4 --- /dev/null +++ b/blake2.h @@ -0,0 +1,107 @@ +/* blake2.h + + The blake2 hash function, see RFC 7693 + + Copyright (C) 2025 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETTLE_BLAKE2_H_INCLUDED +#define NETTLE_BLAKE2_H_INCLUDED + +#include "nettle-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Name mangling */ +#define blake2b_init nettle_blake2b_init +#define blake2b_update nettle_blake2b_update +#define blake2b_digest nettle_blake2b_digest +#define blake2b_512_init nettle_blake2b_512_init +#define blake2s_init nettle_blake2s_init +#define blake2s_update nettle_blake2s_update +#define blake2s_digest nettle_blake2s_digest +#define blake2s_256_init nettle_blake2s_256_init + +#define BLAKE2B_DIGEST_SIZE 64 +#define BLAKE2B_BLOCK_SIZE 128 + +struct blake2b_ctx +{ + uint64_t state[8]; + uint64_t count_low, count_high; /* 128-bit byte count */ + unsigned short digest_size; + unsigned short index; /* index into buffer */ + uint8_t block[BLAKE2B_BLOCK_SIZE]; /* data buffer */ +}; + +void +blake2b_init (struct blake2b_ctx *ctx, unsigned digest_size); + +void +blake2b_update (struct blake2b_ctx *ctx, + size_t length, const uint8_t *data); + +void +blake2b_digest (struct blake2b_ctx *ctx, uint8_t *digest); + +void +blake2b_512_init (struct blake2b_ctx *ctx); + +#define BLAKE2S_DIGEST_SIZE 32 +#define BLAKE2S_BLOCK_SIZE 64 + +struct blake2s_ctx +{ + uint32_t state[8]; + uint64_t count; /* 64-bit byte count */ + unsigned short digest_size; + unsigned short index; /* index into buffer */ + uint8_t block[BLAKE2S_BLOCK_SIZE]; /* data buffer */ +}; + +void +blake2s_init (struct blake2s_ctx *ctx, unsigned digest_size); + +void +blake2s_update (struct blake2s_ctx *ctx, + size_t length, const uint8_t *data); + +void +blake2s_digest (struct blake2s_ctx *ctx, uint8_t *digest); + +void +blake2s_256_init (struct blake2s_ctx *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* NETTLE_BLAKE2_H_INCLUDED */ diff --git a/blake2b.c b/blake2b.c new file mode 100644 index 00000000..153acd33 --- /dev/null +++ b/blake2b.c @@ -0,0 +1,177 @@ +/* blake2b.c + + Copyright (C) 2025 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "blake2.h" +#include "macros.h" +#include "nettle-write.h" + +/* Blake2 mixing function G, similar to a chacha qround. */ +/* R1, R2, R3, R4 = 32, 24, 16, 63, defined as rotations right. */ +#define BLAKE2B_G(x0, x1, x2, x3, w0, w1) do { \ + x0 += x1 + (w0); x3 = ROTL64 (32, (x0 ^ x3)); \ + x2 += x3; x1 = ROTL64 (40, (x1 ^ x2)); \ + x0 += x1 + (w1); x3 = ROTL64 (48, (x0 ^ x3)); \ + x2 += x3; x1 = ROTL64 (1, (x1 ^ x2)); \ + } while (0) + + +/* Same as sha512 H0. */ +static const uint64_t iv[8] = + { + 0x6A09E667F3BCC908ULL,0xBB67AE8584CAA73BULL, + 0x3C6EF372FE94F82BULL,0xA54FF53A5F1D36F1ULL, + 0x510E527FADE682D1ULL,0x9B05688C2B3E6C1FULL, + 0x1F83D9ABFB41BD6BULL,0x5BE0CD19137E2179ULL, + }; + +static void +blake2b_compress (uint64_t *h, const uint8_t *input, + uint64_t count_low, uint64_t count_high, int final) +{ + static const unsigned char sigma[12][16] = + { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + }; + uint64_t v[16]; + uint64_t m[16]; + unsigned i; + + for (i = 0; i < 16; i++, input += 8) + m[i] = LE_READ_UINT64(input); + + for (i = 0; i < 8; i++) + { + v[i] = h[i]; + v[8+i] = iv[i]; + } + v[12] ^= count_low; + v[13] ^= count_high; + v[14] ^= - (uint64_t) final; + + for (i = 0; i < 12; i++) + { + BLAKE2B_G (v[0], v[4], v[ 8], v[12], m[sigma[i][ 0]], m[sigma[i][ 1]]); + BLAKE2B_G (v[1], v[5], v[ 9], v[13], m[sigma[i][ 2]], m[sigma[i][ 3]]); + BLAKE2B_G (v[2], v[6], v[10], v[14], m[sigma[i][ 4]], m[sigma[i][ 5]]); + BLAKE2B_G (v[3], v[7], v[11], v[15], m[sigma[i][ 6]], m[sigma[i][ 7]]); + + BLAKE2B_G (v[0], v[5], v[10], v[15], m[sigma[i][ 8]], m[sigma[i][ 9]]); + BLAKE2B_G (v[1], v[6], v[11], v[12], m[sigma[i][10]], m[sigma[i][11]]); + BLAKE2B_G (v[2], v[7], v[ 8], v[13], m[sigma[i][12]], m[sigma[i][13]]); + BLAKE2B_G (v[3], v[4], v[ 9], v[14], m[sigma[i][14]], m[sigma[i][15]]); + } + for (i = 0; i < 8; i++) + h[i] ^= v[i] ^ v[8+i]; +} + +void +blake2b_init (struct blake2b_ctx *ctx, unsigned digest_size) +{ + assert (digest_size > 0 && digest_size <= BLAKE2B_DIGEST_SIZE); + memcpy (ctx->state, iv, sizeof (ctx->state)); + ctx->state[0] ^= 0x01010000 ^ digest_size; + ctx->count_low = ctx->count_high = ctx->index = 0; + ctx->digest_size = digest_size; +} + +void +blake2b_update (struct blake2b_ctx *ctx, + size_t length, const uint8_t *data) +{ + if (!length) + return; + + /* To be able to pass the finalization flag, don't call the + compression function until we have input exceeding one block. */ + if (ctx->index) + { + unsigned left = BLAKE2B_BLOCK_SIZE - ctx->index; + if (length <= left) + { + memcpy (ctx->block + ctx->index, data, length); + ctx->index += length; + return; + } + memcpy (ctx->block + ctx->index, data, left); + ctx->count_low += BLAKE2B_BLOCK_SIZE; + ctx->count_high += ctx->count_low < BLAKE2B_BLOCK_SIZE; + blake2b_compress (ctx->state, ctx->block, ctx->count_low, ctx->count_high, 0); + data += left; length -= left; + } + for (; length > BLAKE2B_BLOCK_SIZE; + data += BLAKE2B_BLOCK_SIZE, length -= BLAKE2B_BLOCK_SIZE) + { + ctx->count_low += BLAKE2B_BLOCK_SIZE; + ctx->count_high += ctx->count_low < BLAKE2B_BLOCK_SIZE; + blake2b_compress (ctx->state, data, ctx->count_low, ctx->count_high, 0); + } + + memcpy (ctx->block, data, length); + ctx->index = length; +} + +void +blake2b_digest (struct blake2b_ctx *ctx, uint8_t *digest) +{ + memset (ctx->block + ctx->index, 0, BLAKE2B_BLOCK_SIZE - ctx->index); + ctx->count_low += ctx->index; + ctx->count_high += ctx->count_low < ctx->index; + blake2b_compress (ctx->state, ctx->block, ctx->count_low, ctx->count_high, 1); + + assert (ctx->digest_size <= BLAKE2B_DIGEST_SIZE); + _nettle_write_le64 (ctx->digest_size, digest, ctx->state); + + blake2b_init (ctx, ctx->digest_size); +} + +void +blake2b_512_init (struct blake2b_ctx *ctx) +{ + blake2b_init (ctx, BLAKE2B_DIGEST_SIZE); +} diff --git a/blake2s.c b/blake2s.c new file mode 100644 index 00000000..0e585b07 --- /dev/null +++ b/blake2s.c @@ -0,0 +1,168 @@ +/* blake2s.c + + Copyright (C) 2025 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "blake2.h" +#include "macros.h" +#include "nettle-write.h" + +/* Blake2 mixing function G, similar to a chacha qround. */ +/* R1, R2, R3, R4 = 16, 12, 8, 7, defined as rotation right */ +#define BLAKE2S_G(x0, x1, x2, x3, w0, w1) do { \ + x0 += x1 + (w0); x3 = ROTL32 (16, (x0 ^ x3)); \ + x2 += x3; x1 = ROTL32 (20, (x1 ^ x2)); \ + x0 += x1 + (w1); x3 = ROTL32 (24, (x0 ^ x3)); \ + x2 += x3; x1 = ROTL32 (25, (x1 ^ x2)); \ + } while (0) + +/* Same as sha256 H0. */ +static const uint32_t iv[8] = + { + 0x6a09e667UL, 0xbb67ae85UL, 0x3c6ef372UL, 0xa54ff53aUL, + 0x510e527fUL, 0x9b05688cUL, 0x1f83d9abUL, 0x5be0cd19UL, + }; + +static void +blake2s_compress (uint32_t *h, const uint8_t *input, + uint64_t count, int final) +{ + static const unsigned char sigma[10][16] = + { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + }; + uint32_t v[16]; + uint32_t m[16]; + unsigned i; + + for (i = 0; i < 16; i++, input += 4) + m[i] = LE_READ_UINT32(input); + + for (i = 0; i < 8; i++) + { + v[i] = h[i]; + v[8+i] = iv[i]; + } + v[12] ^= count; + v[13] ^= (count >> 32); + v[14] ^= - (uint32_t) final; + + for (i = 0; i < 10; i++) + { + BLAKE2S_G (v[0], v[4], v[ 8], v[12], m[sigma[i][ 0]], m[sigma[i][ 1]]); + BLAKE2S_G (v[1], v[5], v[ 9], v[13], m[sigma[i][ 2]], m[sigma[i][ 3]]); + BLAKE2S_G (v[2], v[6], v[10], v[14], m[sigma[i][ 4]], m[sigma[i][ 5]]); + BLAKE2S_G (v[3], v[7], v[11], v[15], m[sigma[i][ 6]], m[sigma[i][ 7]]); + + BLAKE2S_G (v[0], v[5], v[10], v[15], m[sigma[i][ 8]], m[sigma[i][ 9]]); + BLAKE2S_G (v[1], v[6], v[11], v[12], m[sigma[i][10]], m[sigma[i][11]]); + BLAKE2S_G (v[2], v[7], v[ 8], v[13], m[sigma[i][12]], m[sigma[i][13]]); + BLAKE2S_G (v[3], v[4], v[ 9], v[14], m[sigma[i][14]], m[sigma[i][15]]); + } + for (i = 0; i < 8; i++) + h[i] ^= v[i] ^ v[8+i]; +} + +void +blake2s_init (struct blake2s_ctx *ctx, unsigned digest_size) +{ + assert (digest_size > 0 && digest_size <= BLAKE2S_DIGEST_SIZE); + memcpy (ctx->state, iv, sizeof (ctx->state)); + ctx->state[0] ^= 0x01010000 ^ digest_size; + ctx->count = ctx->index = 0; + ctx->digest_size = digest_size; +} + +void +blake2s_update (struct blake2s_ctx *ctx, + size_t length, const uint8_t *data) +{ + if (!length) + return; + + /* To be able to pass the finalization flag, don't call the + compression function until we have input exceeding one block. */ + if (ctx->index) + { + unsigned left = BLAKE2S_BLOCK_SIZE - ctx->index; + if (length <= left) + { + memcpy (ctx->block + ctx->index, data, length); + ctx->index += length; + return; + } + memcpy (ctx->block + ctx->index, data, left); + ctx->count += BLAKE2S_BLOCK_SIZE; + blake2s_compress (ctx->state, ctx->block, ctx->count, 0); + data += left; length -= left; + } + for (; length > BLAKE2S_BLOCK_SIZE; + data += BLAKE2S_BLOCK_SIZE, length -= BLAKE2S_BLOCK_SIZE) + { + ctx->count += BLAKE2S_BLOCK_SIZE; + blake2s_compress (ctx->state, data, ctx->count, 0); + } + + memcpy (ctx->block, data, length); + ctx->index = length; +} + +void +blake2s_digest (struct blake2s_ctx *ctx, uint8_t *digest) +{ + memset (ctx->block + ctx->index, 0, BLAKE2S_BLOCK_SIZE - ctx->index); + blake2s_compress (ctx->state, ctx->block, ctx->count + ctx->index, 1); + + assert (ctx->digest_size <= BLAKE2S_DIGEST_SIZE); + _nettle_write_le32 (ctx->digest_size, digest, ctx->state); + + blake2s_init (ctx, ctx->digest_size); +} + +void +blake2s_256_init (struct blake2s_ctx *ctx) +{ + blake2s_init (ctx, BLAKE2S_DIGEST_SIZE); +} diff --git a/examples/nettle-benchmark.c b/examples/nettle-benchmark.c index 70c4513e..41041c14 100644 --- a/examples/nettle-benchmark.c +++ b/examples/nettle-benchmark.c @@ -913,6 +913,7 @@ main(int argc, char **argv) &nettle_sha512_224, &nettle_sha512_256, &nettle_sha3_224, &nettle_sha3_256, &nettle_sha3_384, &nettle_sha3_512, + &nettle_blake2s_256, &nettle_blake2b_512, &nettle_ripemd160, &nettle_gosthash94, &nettle_gosthash94cp, &nettle_streebog256, &nettle_streebog512, &nettle_sm3, diff --git a/non-nettle.c b/non-nettle.c index 6ab1a098..3ef3673b 100644 --- a/non-nettle.c +++ b/non-nettle.c @@ -42,6 +42,7 @@ #include "non-nettle.h" #include "arcfour.h" +#include "blake2.h" #include "blowfish.h" #include "cbc.h" #include "chacha.h" @@ -336,3 +337,27 @@ nettle_ocb_aes128_t96 = (nettle_crypt_func *) ocb_aes128_decrypt_wrapper, (nettle_hash_digest_func *) ocb_aes128_t96_digest, }; + +const struct nettle_hash +nettle_blake2b_512 = + { + "blake2b_512", + sizeof (struct blake2b_ctx), + BLAKE2B_DIGEST_SIZE, + BLAKE2B_BLOCK_SIZE, + (nettle_hash_init_func *) blake2b_512_init, + (nettle_hash_update_func *) blake2b_update, + (nettle_hash_digest_func *) blake2b_digest, + }; + +const struct nettle_hash +nettle_blake2s_256 = + { + "blake2s_256", + sizeof (struct blake2s_ctx), + BLAKE2S_DIGEST_SIZE, + BLAKE2S_BLOCK_SIZE, + (nettle_hash_init_func *) blake2s_256_init, + (nettle_hash_update_func *) blake2s_update, + (nettle_hash_digest_func *) blake2s_digest, + }; diff --git a/non-nettle.h b/non-nettle.h index e36c8f9c..056b83b5 100644 --- a/non-nettle.h +++ b/non-nettle.h @@ -92,5 +92,7 @@ struct ocb_aes128_ctx extern const struct nettle_aead nettle_ocb_aes128; extern const struct nettle_aead nettle_ocb_aes128_t96; +extern const struct nettle_hash nettle_blake2b_512; +extern const struct nettle_hash nettle_blake2s_256; #endif /* NETTLE_NON_NETTLE_H_INCLUDED */ diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index b2f0518c..63ed188d 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -13,6 +13,7 @@ PRE_LDFLAGS = -L.. TS_NETTLE_SOURCES = aes-test.c aes-keywrap-test.c arcfour-test.c arctwo-test.c \ balloon-test.c blowfish-test.c bcrypt-test.c cast128-test.c \ base16-test.c base64-test.c \ + blake2b-test.c blake2s-test.c \ camellia-test.c chacha-test.c \ cnd-memcpy-test.c \ des-test.c des3-test.c \ diff --git a/testsuite/blake2b-test.c b/testsuite/blake2b-test.c new file mode 100644 index 00000000..1843cc96 --- /dev/null +++ b/testsuite/blake2b-test.c @@ -0,0 +1,13 @@ +#include "testutils.h" + +#include "non-nettle.h" + +void +test_main(void) +{ + test_hash(&nettle_blake2b_512, SDATA("abc"), + SHEX("BA 80 A5 3F 98 1C 4D 0D 6A 27 97 B6 9F 12 F6 E9" + "4C 21 2F 14 68 5A C4 B7 4B 12 BB 6F DB FF A2 D1" + "7D 87 C5 39 2A AB 79 2D C2 52 D5 DE 45 33 CC 95" + "18 D3 8A A8 DB F1 92 5A B9 23 86 ED D4 00 99 23")); +} diff --git a/testsuite/blake2s-test.c b/testsuite/blake2s-test.c new file mode 100644 index 00000000..2a447284 --- /dev/null +++ b/testsuite/blake2s-test.c @@ -0,0 +1,11 @@ +#include "testutils.h" + +#include "non-nettle.h" + +void +test_main(void) +{ + test_hash(&nettle_blake2s_256, SDATA("abc"), + SHEX("50 8C 5E 8C 32 7C 14 E2 E1 A7 2B A3 4E EB 45 2F" + "37 45 8B 20 9E D6 3A 29 4D 99 9B 4C 86 67 59 82")); +}