Test the implementation of a second hash algorithm with "short" (160-bit) BLAKE2b support. Simply add an additional algorithm to the codebase without changing the use of any hash function and add a test helper. BLAKE2b was chosen simply because it provides a readily accessible hash algorithm with arbitrary length support. A 160-bit (20-byte) hash was chosen to allow identification of tests that depend on the hash algorithm in use while not causing buffer overflows, since parts of the codebase still assume a 20-byte hash. This is a preliminary commit for test purposes only and should not be used in production in any way. --- Makefile | 3 +++ cache.h | 16 ++++++++++++++ hash.h | 9 +++++++- sha1_file.c | 33 ++++++++++++++++++++++++++++ t/helper/test-sblake2b.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 t/helper/test-sblake2b.c diff --git a/Makefile b/Makefile index 1a9b23b679..d46247586b 100644 --- a/Makefile +++ b/Makefile @@ -677,6 +677,7 @@ TEST_PROGRAMS_NEED_X += test-ref-store TEST_PROGRAMS_NEED_X += test-regex TEST_PROGRAMS_NEED_X += test-revision-walking TEST_PROGRAMS_NEED_X += test-run-command +TEST_PROGRAMS_NEED_X += test-sblake2b TEST_PROGRAMS_NEED_X += test-scrap-cache-tree TEST_PROGRAMS_NEED_X += test-sha1 TEST_PROGRAMS_NEED_X += test-sha1-array @@ -1539,6 +1540,8 @@ endif endif endif +EXTLIBS += -lb2 + ifdef SHA1_MAX_BLOCK_SIZE LIB_OBJS += compat/sha1-chunked.o BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)" diff --git a/cache.h b/cache.h index bfde6f757a..638d832350 100644 --- a/cache.h +++ b/cache.h @@ -45,6 +45,10 @@ unsigned long git_deflate_bound(git_zstream *, unsigned long); #define GIT_SHA1_RAWSZ 20 #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ) +/* The length in bytes and in hex digits of an object name (short BLAKE2b value). */ +#define GIT_SBLAKE2B_RAWSZ 20 +#define GIT_SBLAKE2B_HEXSZ (2 * GIT_SBLAKE2B_RAWSZ) + /* The length in byte and in hex digits of the largest possible hash value. */ #define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ #define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ @@ -1013,7 +1017,13 @@ static inline void oidclr(struct object_id *oid) #define EMPTY_TREE_SHA1_BIN_LITERAL \ "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \ "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04" +#define EMPTY_TREE_SBLAKE2B_HEX \ + "f44422a644bfa5212387098f253a1e89eba94548" +#define EMPTY_TREE_SBLAKE2B_BIN_LITERAL \ + "\xf4\x44\x22\xa6\x44\xbf\xa5\x21\x23\x87" \ + "\x09\x8f\x25\x3a\x1e\x89\xeb\xa9\x45\x48" extern const struct object_id empty_tree_oid; +const struct object_id empty_tree_oid_sblake2b; #define EMPTY_TREE_SHA1_BIN (empty_tree_oid.hash) #define EMPTY_BLOB_SHA1_HEX \ @@ -1021,7 +1031,13 @@ extern const struct object_id empty_tree_oid; #define EMPTY_BLOB_SHA1_BIN_LITERAL \ "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \ "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91" +#define EMPTY_BLOB_SBLAKE2B_HEX \ + "a706650a477f63b9b00eba41272bf36ef5a7dfa2" +#define EMPTY_BLOB_SBLAKE2B_BIN_LITERAL \ + "\xa7\x06\x65\x0a\x47\x7f\x63\xb9\xb0\x0e" \ + "\xba\x41\x27\x2b\xf3\x6e\xf5\xa7\xdf\xa2" extern const struct object_id empty_blob_oid; +const struct object_id empty_blob_oid_sblake2b; #define EMPTY_BLOB_SHA1_BIN (empty_blob_oid.hash) diff --git a/hash.h b/hash.h index 365846a6b5..ef510bd51a 100644 --- a/hash.h +++ b/hash.h @@ -15,6 +15,8 @@ #include "block-sha1/sha1.h" #endif +#include <blake2.h> + #ifndef platform_SHA_CTX /* * platform's underlying implementation of SHA-1; could be OpenSSL, @@ -40,6 +42,8 @@ #define git_SHA1_Update git_SHA1_Update_Chunked #endif +#define git_BLAKE2B_CTX blake2b_state + /* * Note that these constants are suitable for indexing the hash_algos array and * comparing against each other, but are otherwise arbitrary, so they should not @@ -52,12 +56,15 @@ #define GIT_HASH_UNKNOWN 0 /* SHA-1 */ #define GIT_HASH_SHA1 1 +/* 20-byte BLAKE2b ("short" BLAKE2b) */ +#define GIT_HASH_SBLAKE2 2 /* Number of algorithms supported (including unknown). */ -#define GIT_HASH_NALGOS (GIT_HASH_SHA1 + 1) +#define GIT_HASH_NALGOS (GIT_HASH_SBLAKE2 + 1) /* A suitably aligned type for stack allocations of hash contexts. */ union git_hash_ctx { git_SHA_CTX sha1; + git_BLAKE2B_CTX blake2b; }; typedef union git_hash_ctx git_hash_ctx; diff --git a/sha1_file.c b/sha1_file.c index d9e2b1f285..5067b22a30 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -38,6 +38,12 @@ const struct object_id empty_tree_oid = { const struct object_id empty_blob_oid = { EMPTY_BLOB_SHA1_BIN_LITERAL }; +const struct object_id empty_tree_oid_sblake2b = { + EMPTY_TREE_SBLAKE2B_BIN_LITERAL +}; +const struct object_id empty_blob_oid_sblake2b = { + EMPTY_BLOB_SBLAKE2B_BIN_LITERAL +}; static void git_hash_sha1_init(void *ctx) { @@ -54,6 +60,21 @@ static void git_hash_sha1_final(unsigned char *hash, void *ctx) git_SHA1_Final(hash, (git_SHA_CTX *)ctx); } +static void git_hash_sblake2b_init(void *ctx) +{ + blake2b_init((git_BLAKE2B_CTX *)ctx, GIT_SBLAKE2B_RAWSZ); +} + +static void git_hash_sblake2b_update(void *ctx, const void *data, size_t len) +{ + blake2b_update((git_BLAKE2B_CTX *)ctx, data, len); +} + +static void git_hash_sblake2b_final(unsigned char *hash, void *ctx) +{ + blake2b_final((git_BLAKE2B_CTX *)ctx, hash, GIT_SBLAKE2B_RAWSZ); +} + static void git_hash_unknown_init(void *ctx) { die("trying to init unknown hash"); @@ -93,6 +114,18 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { &empty_tree_oid, &empty_blob_oid, }, + { + "sblake2b", + /* "sb2b", big-endian */ + 0x73623262, + GIT_SBLAKE2B_RAWSZ, + GIT_SBLAKE2B_HEXSZ, + git_hash_sblake2b_init, + git_hash_sblake2b_update, + git_hash_sblake2b_final, + &empty_tree_oid_sblake2b, + &empty_blob_oid_sblake2b, + } }; /* diff --git a/t/helper/test-sblake2b.c b/t/helper/test-sblake2b.c new file mode 100644 index 0000000000..6623aaa4d5 --- /dev/null +++ b/t/helper/test-sblake2b.c @@ -0,0 +1,56 @@ +#include "cache.h" + +int cmd_main(int ac, const char **av) +{ + git_hash_ctx ctx; + unsigned char hash[GIT_MAX_RAWSZ]; + unsigned bufsz = 8192; + int binary = 0; + char *buffer; + + if (ac == 2) { + if (!strcmp(av[1], "-b")) + binary = 1; + else + bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024; + } + + if (!bufsz) + bufsz = 8192; + + while ((buffer = malloc(bufsz)) == NULL) { + fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz); + bufsz /= 2; + if (bufsz < 1024) + die("OOPS"); + } + + hash_algos[GIT_HASH_SBLAKE2].init_fn(&ctx); + + while (1) { + ssize_t sz, this_sz; + char *cp = buffer; + unsigned room = bufsz; + this_sz = 0; + while (room) { + sz = xread(0, cp, room); + if (sz == 0) + break; + if (sz < 0) + die_errno("test-sha1"); + this_sz += sz; + cp += sz; + room -= sz; + } + if (this_sz == 0) + break; + hash_algos[GIT_HASH_SBLAKE2].update_fn(&ctx, buffer, this_sz); + } + hash_algos[GIT_HASH_SBLAKE2].final_fn(hash, &ctx); + + if (binary) + fwrite(hash, 1, hash_algos[GIT_HASH_SBLAKE2].rawsz, stdout); + else + puts(sha1_to_hex(hash)); + exit(0); +}