From d6fb09240913c9756de5f4a2462062008ebac252 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 5 Nov 2012 12:37:15 -0600 Subject: Win32 CryptoAPI and CNG support for SHA1 --- CMakeLists.txt | 13 +- src/filebuf.c | 6 +- src/global.h | 6 + src/hash.c | 75 ++++------- src/hash.h | 25 +++- src/hash/hash_generic.c | 305 +++++++++++++++++++++++++++++++++++++++++++ src/hash/hash_generic.h | 19 +++ src/hash/hash_openssl.h | 58 ++++++++ src/hash/hash_ppc.c | 91 +++++++++++++ src/hash/hash_ppc.h | 23 ++++ src/hash/hash_ppc_core.S | 224 +++++++++++++++++++++++++++++++ src/hash/hash_win32.c | 276 +++++++++++++++++++++++++++++++++++++++ src/hash/hash_win32.h | 140 ++++++++++++++++++++ src/indexer.c | 28 ++-- src/odb.c | 25 ++-- src/pack-objects.c | 25 ++-- src/ppc/sha1.c | 70 ---------- src/ppc/sha1.h | 26 ---- src/ppc/sha1ppc.S | 224 ------------------------------- src/sha1/sha1.c | 280 --------------------------------------- tests-clar/object/raw/hash.c | 14 +- 21 files changed, 1248 insertions(+), 705 deletions(-) create mode 100644 src/hash/hash_generic.c create mode 100644 src/hash/hash_generic.h create mode 100644 src/hash/hash_openssl.h create mode 100644 src/hash/hash_ppc.c create mode 100644 src/hash/hash_ppc.h create mode 100644 src/hash/hash_ppc_core.S create mode 100644 src/hash/hash_win32.c create mode 100644 src/hash/hash_win32.h delete mode 100644 src/ppc/sha1.c delete mode 100644 src/ppc/sha1.h delete mode 100644 src/ppc/sha1ppc.S delete mode 100644 src/sha1/sha1.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 85f86c00b..bde872fe4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,11 +39,14 @@ ENDIF() # Specify sha1 implementation IF (SHA1_TYPE STREQUAL "ppc") ADD_DEFINITIONS(-DPPC_SHA1) - FILE(GLOB SRC_SHA1 src/ppc/*.c src/ppc/*.S) -ELSEIF (OPENSSL_FOUND) # libcrypto's implementation is faster than ours - ADD_DEFINITIONS(-DOPENSSL_SHA) -ELSE () - FILE(GLOB SRC_SHA1 src/sha1/*.c) + FILE(GLOB SRC_SHA1 src/hash/hash_ppc.c src/hash/hash_ppc_core.S) +ELSEIF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin") + ADD_DEFINITIONS(-DWIN32_SHA1) + FILE(GLOB SRC_SHA1 src/hash/hash_win32.c) +ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin") + ADD_DEFINITIONS(-DOPENSSL_SHA1) +ELSE() + FILE(GLOB SRC_SHA1 src/hash/hash_generic.c) ENDIF() IF (NOT WIN32) diff --git a/src/filebuf.c b/src/filebuf.c index 5e5db0db6..6194fe5e3 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -109,7 +109,7 @@ void git_filebuf_cleanup(git_filebuf *file) p_unlink(file->path_lock); if (file->digest) - git_hash_free_ctx(file->digest); + git_hash_ctx_free(file->digest); if (file->buffer) git__free(file->buffer); @@ -221,7 +221,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags) /* If we are hashing on-write, allocate a new hash context */ if (flags & GIT_FILEBUF_HASH_CONTENTS) { - file->digest = git_hash_new_ctx(); + file->digest = git_hash_ctx_new(); GITERR_CHECK_ALLOC(file->digest); } @@ -299,7 +299,7 @@ int git_filebuf_hash(git_oid *oid, git_filebuf *file) return -1; git_hash_final(oid, file->digest); - git_hash_free_ctx(file->digest); + git_hash_ctx_free(file->digest); file->digest = NULL; return 0; diff --git a/src/global.h b/src/global.h index 0ad41ee63..b9f8b6773 100644 --- a/src/global.h +++ b/src/global.h @@ -8,10 +8,16 @@ #define INCLUDE_global_h__ #include "mwindow.h" +#include "hash.h" typedef struct { git_error *last_error; git_error error_t; + +#ifdef WIN32_SHA1 + git_hash_prov hash_prov; +#endif + } git_global_st; git_global_st *git__global_state(void); diff --git a/src/hash.c b/src/hash.c index 460756913..336030d41 100644 --- a/src/hash.c +++ b/src/hash.c @@ -8,67 +8,40 @@ #include "common.h" #include "hash.h" -#if defined(PPC_SHA1) -# include "ppc/sha1.h" -#else -# include "sha1.h" -#endif - -struct git_hash_ctx { - SHA_CTX c; -}; - -git_hash_ctx *git_hash_new_ctx(void) +int git_hash_buf(git_oid *out, const void *data, size_t len) { - git_hash_ctx *ctx = git__malloc(sizeof(*ctx)); - - if (!ctx) - return NULL; - - SHA1_Init(&ctx->c); + git_hash_ctx *ctx; + int error = 0; - return ctx; -} + if ((ctx = git_hash_ctx_new()) == NULL) + return -1; -void git_hash_free_ctx(git_hash_ctx *ctx) -{ - git__free(ctx); -} + if ((error = git_hash_update(ctx, data, len)) >= 0) + error = git_hash_final(out, ctx); -void git_hash_init(git_hash_ctx *ctx) -{ - assert(ctx); - SHA1_Init(&ctx->c); + git_hash_ctx_free(ctx); + + return error; } -void git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) +int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n) { - assert(ctx); - SHA1_Update(&ctx->c, data, len); -} + git_hash_ctx *ctx; + size_t i; + int error = 0; -void git_hash_final(git_oid *out, git_hash_ctx *ctx) -{ - assert(ctx); - SHA1_Final(out->id, &ctx->c); -} + if ((ctx = git_hash_ctx_new()) == NULL) + return -1; -void git_hash_buf(git_oid *out, const void *data, size_t len) -{ - SHA_CTX c; + for (i = 0; i < n; i++) { + if ((error = git_hash_update(ctx, vec[i].data, vec[i].len)) < 0) + goto done; + } - SHA1_Init(&c); - SHA1_Update(&c, data, len); - SHA1_Final(out->id, &c); -} + error = git_hash_final(out, ctx); -void git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n) -{ - SHA_CTX c; - size_t i; +done: + git_hash_ctx_free(ctx); - SHA1_Init(&c); - for (i = 0; i < n; i++) - SHA1_Update(&c, vec[i].data, vec[i].len); - SHA1_Final(out->id, &c); + return error; } diff --git a/src/hash.h b/src/hash.h index 33d7b20cd..2a9e19837 100644 --- a/src/hash.h +++ b/src/hash.h @@ -9,21 +9,32 @@ #include "git2/oid.h" +typedef struct git_hash_prov git_hash_prov; typedef struct git_hash_ctx git_hash_ctx; +#if defined(OPENSSL_SHA1) +# include "hash/hash_openssl.h" +#elif defined(WIN32_SHA1) +# include "hash/hash_win32.h" +#elif defined(PPC_SHA1) +# include "hash/hash_ppc.h" +#else +# include "hash/hash_generic.h" +#endif + typedef struct { void *data; size_t len; } git_buf_vec; -git_hash_ctx *git_hash_new_ctx(void); -void git_hash_free_ctx(git_hash_ctx *ctx); +git_hash_ctx *git_hash_ctx_new(void); +void git_hash_ctx_free(git_hash_ctx *ctx); -void git_hash_init(git_hash_ctx *c); -void git_hash_update(git_hash_ctx *c, const void *data, size_t len); -void git_hash_final(git_oid *out, git_hash_ctx *c); +int git_hash_init(git_hash_ctx *c); +int git_hash_update(git_hash_ctx *c, const void *data, size_t len); +int git_hash_final(git_oid *out, git_hash_ctx *c); -void git_hash_buf(git_oid *out, const void *data, size_t len); -void git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n); +int git_hash_buf(git_oid *out, const void *data, size_t len); +int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n); #endif /* INCLUDE_hash_h__ */ diff --git a/src/hash/hash_generic.c b/src/hash/hash_generic.c new file mode 100644 index 000000000..cab5469d7 --- /dev/null +++ b/src/hash/hash_generic.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2009-2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "common.h" +#include "hash.h" +#include "hash/hash_generic.h" + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +/* + * Force usage of rol or ror by selecting the one with the smaller constant. + * It _can_ generate slightly smaller code (a constant of 1 is special), but + * perhaps more importantly it's possibly faster on any uarch that does a + * rotate with a loop. + */ + +#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }) +#define SHA_ROL(x,n) SHA_ASM("rol", x, n) +#define SHA_ROR(x,n) SHA_ASM("ror", x, n) + +#else + +#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r))) +#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n)) +#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n) + +#endif + +/* + * If you have 32 registers or more, the compiler can (and should) + * try to change the array[] accesses into registers. However, on + * machines with less than ~25 registers, that won't really work, + * and at least gcc will make an unholy mess of it. + * + * So to avoid that mess which just slows things down, we force + * the stores to memory to actually happen (we might be better off + * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as + * suggested by Artur Skawina - that will also make gcc unable to + * try to do the silly "optimize away loads" part because it won't + * see what the value will be). + * + * Ben Herrenschmidt reports that on PPC, the C version comes close + * to the optimized asm with this (ie on PPC you don't want that + * 'volatile', since there are lots of registers). + * + * On ARM we get the best code generation by forcing a full memory barrier + * between each SHA_ROUND, otherwise gcc happily get wild with spilling and + * the stack frame size simply explode and performance goes down the drain. + */ + +#if defined(__i386__) || defined(__x86_64__) + #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val)) +#elif defined(__GNUC__) && defined(__arm__) + #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0) +#else + #define setW(x, val) (W(x) = (val)) +#endif + +/* + * Performance might be improved if the CPU architecture is OK with + * unaligned 32-bit loads and a fast ntohl() is available. + * Otherwise fall back to byte loads and shifts which is portable, + * and is faster on architectures with memory alignment issues. + */ + +#if defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64) || \ + defined(__ppc__) || defined(__ppc64__) || \ + defined(__powerpc__) || defined(__powerpc64__) || \ + defined(__s390__) || defined(__s390x__) + +#define get_be32(p) ntohl(*(const unsigned int *)(p)) +#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) + +#else + +#define get_be32(p) ( \ + (*((const unsigned char *)(p) + 0) << 24) | \ + (*((const unsigned char *)(p) + 1) << 16) | \ + (*((const unsigned char *)(p) + 2) << 8) | \ + (*((const unsigned char *)(p) + 3) << 0) ) +#define put_be32(p, v) do { \ + unsigned int __v = (v); \ + *((unsigned char *)(p) + 0) = __v >> 24; \ + *((unsigned char *)(p) + 1) = __v >> 16; \ + *((unsigned char *)(p) + 2) = __v >> 8; \ + *((unsigned char *)(p) + 3) = __v >> 0; } while (0) + +#endif + +/* This "rolls" over the 512-bit array */ +#define W(x) (array[(x)&15]) + +/* + * Where do we get the source from? The first 16 iterations get it from + * the input data, the next mix it from the 512-bit array. + */ +#define SHA_SRC(t) get_be32(data + t) +#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) + +#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ + unsigned int TEMP = input(t); setW(t, TEMP); \ + E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \ + B = SHA_ROR(B, 2); } while (0) + +#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E ) +#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) +#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E ) + +static void hash__block(git_hash_ctx *ctx, const unsigned int *data) +{ + unsigned int A,B,C,D,E; + unsigned int array[16]; + + A = ctx->H[0]; + B = ctx->H[1]; + C = ctx->H[2]; + D = ctx->H[3]; + E = ctx->H[4]; + + /* Round 1 - iterations 0-16 take their input from 'data' */ + T_0_15( 0, A, B, C, D, E); + T_0_15( 1, E, A, B, C, D); + T_0_15( 2, D, E, A, B, C); + T_0_15( 3, C, D, E, A, B); + T_0_15( 4, B, C, D, E, A); + T_0_15( 5, A, B, C, D, E); + T_0_15( 6, E, A, B, C, D); + T_0_15( 7, D, E, A, B, C); + T_0_15( 8, C, D, E, A, B); + T_0_15( 9, B, C, D, E, A); + T_0_15(10, A, B, C, D, E); + T_0_15(11, E, A, B, C, D); + T_0_15(12, D, E, A, B, C); + T_0_15(13, C, D, E, A, B); + T_0_15(14, B, C, D, E, A); + T_0_15(15, A, B, C, D, E); + + /* Round 1 - tail. Input from 512-bit mixing array */ + T_16_19(16, E, A, B, C, D); + T_16_19(17, D, E, A, B, C); + T_16_19(18, C, D, E, A, B); + T_16_19(19, B, C, D, E, A); + + /* Round 2 */ + T_20_39(20, A, B, C, D, E); + T_20_39(21, E, A, B, C, D); + T_20_39(22, D, E, A, B, C); + T_20_39(23, C, D, E, A, B); + T_20_39(24, B, C, D, E, A); + T_20_39(25, A, B, C, D, E); + T_20_39(26, E, A, B, C, D); + T_20_39(27, D, E, A, B, C); + T_20_39(28, C, D, E, A, B); + T_20_39(29, B, C, D, E, A); + T_20_39(30, A, B, C, D, E); + T_20_39(31, E, A, B, C, D); + T_20_39(32, D, E, A, B, C); + T_20_39(33, C, D, E, A, B); + T_20_39(34, B, C, D, E, A); + T_20_39(35, A, B, C, D, E); + T_20_39(36, E, A, B, C, D); + T_20_39(37, D, E, A, B, C); + T_20_39(38, C, D, E, A, B); + T_20_39(39, B, C, D, E, A); + + /* Round 3 */ + T_40_59(40, A, B, C, D, E); + T_40_59(41, E, A, B, C, D); + T_40_59(42, D, E, A, B, C); + T_40_59(43, C, D, E, A, B); + T_40_59(44, B, C, D, E, A); + T_40_59(45, A, B, C, D, E); + T_40_59(46, E, A, B, C, D); + T_40_59(47, D, E, A, B, C); + T_40_59(48, C, D, E, A, B); + T_40_59(49, B, C, D, E, A); + T_40_59(50, A, B, C, D, E); + T_40_59(51, E, A, B, C, D); + T_40_59(52, D, E, A, B, C); + T_40_59(53, C, D, E, A, B); + T_40_59(54, B, C, D, E, A); + T_40_59(55, A, B, C, D, E); + T_40_59(56, E, A, B, C, D); + T_40_59(57, D, E, A, B, C); + T_40_59(58, C, D, E, A, B); + T_40_59(59, B, C, D, E, A); + + /* Round 4 */ + T_60_79(60, A, B, C, D, E); + T_60_79(61, E, A, B, C, D); + T_60_79(62, D, E, A, B, C); + T_60_79(63, C, D, E, A, B); + T_60_79(64, B, C, D, E, A); + T_60_79(65, A, B, C, D, E); + T_60_79(66, E, A, B, C, D); + T_60_79(67, D, E, A, B, C); + T_60_79(68, C, D, E, A, B); + T_60_79(69, B, C, D, E, A); + T_60_79(70, A, B, C, D, E); + T_60_79(71, E, A, B, C, D); + T_60_79(72, D, E, A, B, C); + T_60_79(73, C, D, E, A, B); + T_60_79(74, B, C, D, E, A); + T_60_79(75, A, B, C, D, E); + T_60_79(76, E, A, B, C, D); + T_60_79(77, D, E, A, B, C); + T_60_79(78, C, D, E, A, B); + T_60_79(79, B, C, D, E, A); + + ctx->H[0] += A; + ctx->H[1] += B; + ctx->H[2] += C; + ctx->H[3] += D; + ctx->H[4] += E; +} + +git_hash_ctx *git_hash_ctx_new(void) +{ + git_hash_ctx *ctx = git__malloc(sizeof(git_hash_ctx)); + + if (!ctx) + return NULL; + + git_hash_init(ctx); + + return ctx; +} + +int git_hash_init(git_hash_ctx *ctx) +{ + ctx->size = 0; + + /* Initialize H with the magic constants (see FIPS180 for constants) */ + ctx->H[0] = 0x67452301; + ctx->H[1] = 0xefcdab89; + ctx->H[2] = 0x98badcfe; + ctx->H[3] = 0x10325476; + ctx->H[4] = 0xc3d2e1f0; + + return 0; +} + +int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + unsigned int lenW = ctx->size & 63; + + ctx->size += len; + + /* Read the data into W and process blocks as they get full */ + if (lenW) { + unsigned int left = 64 - lenW; + if (len < left) + left = (unsigned int)len; + memcpy(lenW + (char *)ctx->W, data, left); + lenW = (lenW + left) & 63; + len -= left; + data = ((const char *)data + left); + if (lenW) + return 0; + hash__block(ctx, ctx->W); + } + while (len >= 64) { + hash__block(ctx, data); + data = ((const char *)data + 64); + len -= 64; + } + if (len) + memcpy(ctx->W, data, len); + + return 0; +} + +int git_hash_final(git_oid *out, git_hash_ctx *ctx) +{ + static const unsigned char pad[64] = { 0x80 }; + unsigned int padlen[2]; + int i; + + /* Pad with a binary 1 (ie 0x80), then zeroes, then length */ + padlen[0] = htonl((uint32_t)(ctx->size >> 29)); + padlen[1] = htonl((uint32_t)(ctx->size << 3)); + + i = ctx->size & 63; + git_hash_update(ctx, pad, 1+ (63 & (55 - i))); + git_hash_update(ctx, padlen, 8); + + /* Output hash */ + for (i = 0; i < 5; i++) + put_be32(out->id + i*4, ctx->H[i]); + + return 0; +} + +void git_hash_ctx_free(git_hash_ctx *ctx) +{ + if (ctx) + git__free(ctx); +} diff --git a/src/hash/hash_generic.h b/src/hash/hash_generic.h new file mode 100644 index 000000000..400c7edcc --- /dev/null +++ b/src/hash/hash_generic.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2009-2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef INCLUDE_hash_generic_h__ +#define INCLUDE_hash_generic_h__ + +#include "hash.h" + +struct git_hash_ctx { + unsigned long long size; + unsigned int H[5]; + unsigned int W[16]; +}; + +#endif /* INCLUDE_hash_generic_h__ */ diff --git a/src/hash/hash_openssl.h b/src/hash/hash_openssl.h new file mode 100644 index 000000000..b416db50c --- /dev/null +++ b/src/hash/hash_openssl.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009-2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef INCLUDE_hash_openssl_h__ +#define INCLUDE_hash_openssl_h__ + +#include "hash.h" + +#include + +struct git_hash_ctx { + SHA_CTX c; +}; + +GIT_INLINE(git_hash_ctx *) git_hash_ctx_new(void) +{ + git_hash_ctx *ctx = git__malloc(sizeof(git_hash_ctx)); + + if (!ctx) + return NULL; + + SHA1_Init(&ctx->c); + + return ctx; +} + +GIT_INLINE(void) git_hash_ctx_free(git_hash_ctx *ctx) +{ + if (ctx) + git__free(ctx); +} + +GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx) +{ + assert(ctx); + SHA1_Init(&ctx->c); + return 0; +} + +GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + assert(ctx); + SHA1_Update(&ctx->c, data, len); + return 0; +} + +GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx) +{ + assert(ctx); + SHA1_Final(out->id, &ctx->c); + return 0; +} + +#endif /* INCLUDE_hash_openssl_h__ */ diff --git a/src/hash/hash_ppc.c b/src/hash/hash_ppc.c new file mode 100644 index 000000000..95ad3b1a1 --- /dev/null +++ b/src/hash/hash_ppc.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2009-2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include +#include + +#include "common.h" +#include "hash.h" + +extern void hash_ppc_core(uint32_t *hash, const unsigned char *p, + unsigned int nblocks); + +git_hash_ctx *git_hash_ctx_new(void) +{ + git_hash_ctx *ctx = git__malloc(sizeof(git_hash_ctx)); + + if (!ctx) + return NULL; + + git_hash_init(ctx); + + return ctx; +} + +int git_hash_init(git_hash_ctx *c) +{ + c->hash[0] = 0x67452301; + c->hash[1] = 0xEFCDAB89; + c->hash[2] = 0x98BADCFE; + c->hash[3] = 0x10325476; + c->hash[4] = 0xC3D2E1F0; + c->len = 0; + c->cnt = 0; + return 0; +} + +int git_hash_update(git_hash_ctx *c, const void *ptr, size_t n) +{ + unsigned long nb; + const unsigned char *p = ptr; + + c->len += (uint64_t) n << 3; + while (n != 0) { + if (c->cnt || n < 64) { + nb = 64 - c->cnt; + if (nb > n) + nb = n; + memcpy(&c->buf.b[c->cnt], p, nb); + if ((c->cnt += nb) == 64) { + hash_ppc_core(c->hash, c->buf.b, 1); + c->cnt = 0; + } + } else { + nb = n >> 6; + hash_ppc_core(c->hash, p, nb); + nb <<= 6; + } + n -= nb; + p += nb; + } + return 0; +} + +int git_hash_final(git_oid *oid, git_hash_ctx *c) +{ + unsigned int cnt = c->cnt; + + c->buf.b[cnt++] = 0x80; + if (cnt > 56) { + if (cnt < 64) + memset(&c->buf.b[cnt], 0, 64 - cnt); + hash_ppc_core(c->hash, c->buf.b, 1); + cnt = 0; + } + if (cnt < 56) + memset(&c->buf.b[cnt], 0, 56 - cnt); + c->buf.l[7] = c->len; + hash_ppc_core(c->hash, c->buf.b, 1); + memcpy(oid->id, c->hash, 20); + return 0; +} + +void git_hash_ctx_free(git_hash_ctx *ctx) +{ + if (ctx) + git__free(ctx); +} diff --git a/src/hash/hash_ppc.h b/src/hash/hash_ppc.h new file mode 100644 index 000000000..200d19310 --- /dev/null +++ b/src/hash/hash_ppc.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2009-2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef INCLUDE_hash_ppc_h__ +#define INCLUDE_hash_ppc_h__ + +#include + +struct git_hash_ctx { + uint32_t hash[5]; + uint32_t cnt; + uint64_t len; + union { + unsigned char b[64]; + uint64_t l[8]; + } buf; +}; + +#endif /* INCLUDE_hash_generic_h__ */ diff --git a/src/hash/hash_ppc_core.S b/src/hash/hash_ppc_core.S new file mode 100644 index 000000000..1de816cf5 --- /dev/null +++ b/src/hash/hash_ppc_core.S @@ -0,0 +1,224 @@ +/* + * SHA-1 implementation for PowerPC. + * + * Copyright (C) 2005 Paul Mackerras + */ + +/* + * PowerPC calling convention: + * %r0 - volatile temp + * %r1 - stack pointer. + * %r2 - reserved + * %r3-%r12 - Incoming arguments & return values; volatile. + * %r13-%r31 - Callee-save registers + * %lr - Return address, volatile + * %ctr - volatile + * + * Register usage in this routine: + * %r0 - temp + * %r3 - argument (pointer to 5 words of SHA state) + * %r4 - argument (pointer to data to hash) + * %r5 - Constant K in SHA round (initially number of blocks to hash) + * %r6-%r10 - Working copies of SHA variables A..E (actually E..A order) + * %r11-%r26 - Data being hashed W[]. + * %r27-%r31 - Previous copies of A..E, for final add back. + * %ctr - loop count + */ + + +/* + * We roll the registers for A, B, C, D, E around on each + * iteration; E on iteration t is D on iteration t+1, and so on. + * We use registers 6 - 10 for this. (Registers 27 - 31 hold + * the previous values.) + */ +#define RA(t) (((t)+4)%5+6) +#define RB(t) (((t)+3)%5+6) +#define RC(t) (((t)+2)%5+6) +#define RD(t) (((t)+1)%5+6) +#define RE(t) (((t)+0)%5+6) + +/* We use registers 11 - 26 for the W values */ +#define W(t) ((t)%16+11) + +/* Register 5 is used for the constant k */ + +/* + * The basic SHA-1 round function is: + * E += ROTL(A,5) + F(B,C,D) + W[i] + K; B = ROTL(B,30) + * Then the variables are renamed: (A,B,C,D,E) = (E,A,B,C,D). + * + * Every 20 rounds, the function F() and the constant K changes: + * - 20 rounds of f0(b,c,d) = "bit wise b ? c : d" = (^b & d) + (b & c) + * - 20 rounds of f1(b,c,d) = b^c^d = (b^d)^c + * - 20 rounds of f2(b,c,d) = majority(b,c,d) = (b&d) + ((b^d)&c) + * - 20 more rounds of f1(b,c,d) + * + * These are all scheduled for near-optimal performance on a G4. + * The G4 is a 3-issue out-of-order machine with 3 ALUs, but it can only + * *consider* starting the oldest 3 instructions per cycle. So to get + * maximum performance out of it, you have to treat it as an in-order + * machine. Which means interleaving the computation round t with the + * computation of W[t+4]. + * + * The first 16 rounds use W values loaded directly from memory, while the + * remaining 64 use values computed from those first 16. We preload + * 4 values before starting, so there are three kinds of rounds: + * - The first 12 (all f0) also load the W values from memory. + * - The next 64 compute W(i+4) in parallel. 8*f0, 20*f1, 20*f2, 16*f1. + * - The last 4 (all f1) do not do anything with W. + * + * Therefore, we have 6 different round functions: + * STEPD0_LOAD(t,s) - Perform round t and load W(s). s < 16 + * STEPD0_UPDATE(t,s) - Perform round t and compute W(s). s >= 16. + * STEPD1_UPDATE(t,s) + * STEPD2_UPDATE(t,s) + * STEPD1(t) - Perform round t with no load or update. + * + * The G5 is more fully out-of-order, and can find the parallelism + * by itself. The big limit is that it has a 2-cycle ALU latency, so + * even though it's 2-way, the code has to be scheduled as if it's + * 4-way, which can be a limit. To help it, we try to schedule the + * read of RA(t) as late as possible so it doesn't stall waiting for + * the previous round's RE(t-1), and we try to rotate RB(t) as early + * as possible while reading RC(t) (= RB(t-1)) as late as possible. + */ + +/* the initial loads. */ +#define LOADW(s) \ + lwz W(s),(s)*4(%r4) + +/* + * Perform a step with F0, and load W(s). Uses W(s) as a temporary + * before loading it. + * This is actually 10 instructions, which is an awkward fit. + * It can execute grouped as listed, or delayed one instruction. + * (If delayed two instructions, there is a stall before the start of the + * second line.) Thus, two iterations take 7 cycles, 3.5 cycles per round. + */ +#define STEPD0_LOAD(t,s) \ +add RE(t),RE(t),W(t); andc %r0,RD(t),RB(t); and W(s),RC(t),RB(t); \ +add RE(t),RE(t),%r0; rotlwi %r0,RA(t),5; rotlwi RB(t),RB(t),30; \ +add RE(t),RE(t),W(s); add %r0,%r0,%r5; lwz W(s),(s)*4(%r4); \ +add RE(t),RE(t),%r0 + +/* + * This is likewise awkward, 13 instructions. However, it can also + * execute starting with 2 out of 3 possible moduli, so it does 2 rounds + * in 9 cycles, 4.5 cycles/round. + */ +#define STEPD0_UPDATE(t,s,loadk...) \ +add RE(t),RE(t),W(t); andc %r0,RD(t),RB(t); xor W(s),W((s)-16),W((s)-3); \ +add RE(t),RE(t),%r0; and %r0,RC(t),RB(t); xor W(s),W(s),W((s)-8); \ +add RE(t),RE(t),%r0; rotlwi %r0,RA(t),5; xor W(s),W(s),W((s)-14); \ +add RE(t),RE(t),%r5; loadk; rotlwi RB(t),RB(t),30; rotlwi W(s),W(s),1; \ +add RE(t),RE(t),%r0 + +/* Nicely optimal. Conveniently, also the most common. */ +#define STEPD1_UPDATE(t,s,loadk...) \ +add RE(t),RE(t),W(t); xor %r0,RD(t),RB(t); xor W(s),W((s)-16),W((s)-3); \ +add RE(t),RE(t),%r5; loadk; xor %r0,%r0,RC(t); xor W(s),W(s),W((s)-8); \ +add RE(t),RE(t),%r0; rotlwi %r0,RA(t),5; xor W(s),W(s),W((s)-14); \ +add RE(t),RE(t),%r0; rotlwi RB(t),RB(t),30; rotlwi W(s),W(s),1 + +/* + * The naked version, no UPDATE, for the last 4 rounds. 3 cycles per. + * We could use W(s) as a temp register, but we don't need it. + */ +#define STEPD1(t) \ + add RE(t),RE(t),W(t); xor %r0,RD(t),RB(t); \ +rotlwi RB(t),RB(t),30; add RE(t),RE(t),%r5; xor %r0,%r0,RC(t); \ +add RE(t),RE(t),%r0; rotlwi %r0,RA(t),5; /* spare slot */ \ +add RE(t),RE(t),%r0 + +/* + * 14 instructions, 5 cycles per. The majority function is a bit + * awkward to compute. This can execute with a 1-instruction delay, + * but it causes a 2-instruction delay, which triggers a stall. + */ +#define STEPD2_UPDATE(t,s,loadk...) \ +add RE(t),RE(t),W(t); and %r0,RD(t),RB(t); xor W(s),W((s)-16),W((s)-3); \ +add RE(t),RE(t),%r0; xor %r0,RD(t),RB(t); xor W(s),W(s),W((s)-8); \ +add RE(t),RE(t),%r5; loadk; and %r0,%r0,RC(t); xor W(s),W(s),W((s)-14); \ +add RE(t),RE(t),%r0; rotlwi %r0,RA(t),5; rotlwi W(s),W(s),1; \ +add RE(t),RE(t),%r0; rotlwi RB(t),RB(t),30 + +#define STEP0_LOAD4(t,s) \ + STEPD0_LOAD(t,s); \ + STEPD0_LOAD((t+1),(s)+1); \ + STEPD0_LOAD((t)+2,(s)+2); \ + STEPD0_LOAD((t)+3,(s)+3) + +#define STEPUP4(fn, t, s, loadk...) \ + STEP##fn##_UPDATE(t,s,); \ + STEP##fn##_UPDATE((t)+1,(s)+1,); \ + STEP##fn##_UPDATE((t)+2,(s)+2,); \ + STEP##fn##_UPDATE((t)+3,(s)+3,loadk) + +#define STEPUP20(fn, t, s, loadk...) \ + STEPUP4(fn, t, s,); \ + STEPUP4(fn, (t)+4, (s)+4,); \ + STEPUP4(fn, (t)+8, (s)+8,); \ + STEPUP4(fn, (t)+12, (s)+12,); \ + STEPUP4(fn, (t)+16, (s)+16, loadk) + + .globl hash_ppc_core +hash_ppc_core: + stwu %r1,-80(%r1) + stmw %r13,4(%r1) + + /* Load up A - E */ + lmw %r27,0(%r3) + + mtctr %r5 + +1: + LOADW(0) + lis %r5,0x5a82 + mr RE(0),%r31 + LOADW(1) + mr RD(0),%r30 + mr RC(0),%r29 + LOADW(2) + ori %r5,%r5,0x7999 /* K0-19 */ + mr RB(0),%r28 + LOADW(3) + mr RA(0),%r27 + + STEP0_LOAD4(0, 4) + STEP0_LOAD4(4, 8) + STEP0_LOAD4(8, 12) + STEPUP4(D0, 12, 16,) + STEPUP4(D0, 16, 20, lis %r5,0x6ed9) + + ori %r5,%r5,0xeba1 /* K20-39 */ + STEPUP20(D1, 20, 24, lis %r5,0x8f1b) + + ori %r5,%r5,0xbcdc /* K40-59 */ + STEPUP20(D2, 40, 44, lis %r5,0xca62) + + ori %r5,%r5,0xc1d6 /* K60-79 */ + STEPUP4(D1, 60, 64,) + STEPUP4(D1, 64, 68,) + STEPUP4(D1, 68, 72,) + STEPUP4(D1, 72, 76,) + addi %r4,%r4,64 + STEPD1(76) + STEPD1(77) + STEPD1(78) + STEPD1(79) + + /* Add results to original values */ + add %r31,%r31,RE(0) + add %r30,%r30,RD(0) + add %r29,%r29,RC(0) + add %r28,%r28,RB(0) + add %r27,%r27,RA(0) + + bdnz 1b + + /* Save final hash, restore registers, and return */ + stmw %r27,0(%r3) + lmw %r13,4(%r1) + addi %r1,%r1,80 + blr diff --git a/src/hash/hash_win32.c b/src/hash/hash_win32.c new file mode 100644 index 000000000..26b3554b5 --- /dev/null +++ b/src/hash/hash_win32.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2009-2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "common.h" +#include "global.h" +#include "hash.h" +#include "hash/hash_win32.h" + +#include +#include + +/* Initialize CNG, if available */ +GIT_INLINE(int) hash_cng_prov_init(git_hash_prov *prov) +{ + OSVERSIONINFOEX version_test = {0}; + DWORD version_test_mask; + DWORDLONG version_condition_mask = 0; + char dll_path[MAX_PATH]; + DWORD dll_path_len, size_len; + + /* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */ + version_test.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + version_test.dwMajorVersion = 6; + version_test.dwMinorVersion = 0; + version_test.wServicePackMajor = 1; + version_test.wServicePackMinor = 0; + + version_test_mask = (VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR); + + VER_SET_CONDITION(version_condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(version_condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); + + if (!VerifyVersionInfo(&version_test, version_test_mask, version_condition_mask)) + return -1; + + /* Load bcrypt.dll explicitly from the system directory */ + if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 || dll_path_len > MAX_PATH || + StringCchCat(dll_path, MAX_PATH, "\\") < 0 || + StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 || + (prov->prov.cng.dll = LoadLibrary(dll_path)) == NULL) + return -1; + + /* Load the function addresses */ + if ((prov->prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(prov->prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL || + (prov->prov.cng.get_property = (hash_win32_cng_get_property_fn)GetProcAddress(prov->prov.cng.dll, "BCryptGetProperty")) == NULL || + (prov->prov.cng.create_hash = (hash_win32_cng_create_hash_fn)GetProcAddress(prov->prov.cng.dll, "BCryptCreateHash")) == NULL || + (prov->prov.cng.finish_hash = (hash_win32_cng_finish_hash_fn)GetProcAddress(prov->prov.cng.dll, "BCryptFinishHash")) == NULL || + (prov->prov.cng.hash_data = (hash_win32_cng_hash_data_fn)GetProcAddress(prov->prov.cng.dll, "BCryptHashData")) == NULL || + (prov->prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(prov->prov.cng.dll, "BCryptDestroyHash")) == NULL || + (prov->prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(prov->prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) { + FreeLibrary(prov->prov.cng.dll); + return -1; + } + + /* Load the SHA1 algorithm */ + if (prov->prov.cng.open_algorithm_provider(&prov->prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) { + FreeLibrary(prov->prov.cng.dll); + return -1; + } + + /* Get storage space for the hash object */ + if (prov->prov.cng.get_property(prov->prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&prov->prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) { + prov->prov.cng.close_algorithm_provider(prov->prov.cng.handle, 0); + FreeLibrary(prov->prov.cng.dll); + return -1; + } + + prov->type = CNG; + return 0; +} + +/* Initialize CryptoAPI */ +GIT_INLINE(int) hash_cryptoapi_prov_init(git_hash_prov *prov) +{ + if (!CryptAcquireContext(&prov->prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + return -1; + + prov->type = CRYPTOAPI; + return 0; +} + +static int hash_win32_prov_init(git_hash_prov *prov) +{ + int error = 0; + + assert(prov->type == INVALID); + + /* Try to load CNG */ + if ((error = hash_cng_prov_init(prov)) < 0) + error = hash_cryptoapi_prov_init(prov); + + return error; +} + +/* CryptoAPI: available in Windows XP and newer */ + +GIT_INLINE(git_hash_ctx *) hash_ctx_cryptoapi_new(git_hash_prov *prov) +{ + git_hash_ctx *ctx; + + if ((ctx = git__calloc(1, sizeof(git_hash_ctx))) == NULL) + return NULL; + + ctx->type = CRYPTOAPI; + ctx->prov = prov; + + if (git_hash_init(ctx) < 0) { + git__free(ctx); + return NULL; + } + + return ctx; +} + +GIT_INLINE(int) hash_cryptoapi_init(git_hash_ctx *ctx) +{ + if (ctx->ctx.cryptoapi.valid) + CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle); + + if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) { + ctx->ctx.cryptoapi.valid = 0; + return -1; + } + + ctx->ctx.cryptoapi.valid = 1; + return 0; +} + +GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + assert(ctx->ctx.cryptoapi.valid); + + if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, (const BYTE *)data, len, 0)) + return -1; + + return 0; +} + +GIT_INLINE(int) hash_cryptoapi_final(git_oid *out, git_hash_ctx *ctx) +{ + DWORD len = 20; + int error = 0; + + assert(ctx->ctx.cryptoapi.valid); + + if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out->id, &len, 0)) + error = -1; + + CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle); + ctx->ctx.cryptoapi.valid = 0; + + return error; +} + +GIT_INLINE(void) hash_cryptoapi_free(git_hash_ctx *ctx) +{ + if (ctx->ctx.cryptoapi.valid) + CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle); +} + +/* CNG: Available in Windows Server 2008 and newer */ + +GIT_INLINE(git_hash_ctx *) hash_ctx_cng_new(git_hash_prov *prov) +{ + git_hash_ctx *ctx; + + if ((ctx = git__calloc(1, sizeof(git_hash_ctx))) == NULL || + (ctx->ctx.cng.hash_object = git__malloc(prov->prov.cng.hash_object_size)) == NULL) + return NULL; + + if (prov->prov.cng.create_hash(prov->prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, prov->prov.cng.hash_object_size, NULL, 0, 0) < 0) { + git__free(ctx->ctx.cng.hash_object); + git__free(ctx); + return NULL; + } + + ctx->type = CNG; + ctx->prov = prov; + + return ctx; +} + +GIT_INLINE(int) hash_cng_init(git_hash_ctx *ctx) +{ + BYTE hash[GIT_OID_RAWSZ]; + + if (!ctx->ctx.cng.updated) + return 0; + + /* CNG needs to be finished to restart */ + if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0) + return -1; + + ctx->ctx.cng.updated = 0; + + return 0; +} + +GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, (PBYTE)data, len, 0) < 0) + return -1; + + return 0; +} + +GIT_INLINE(int) hash_cng_final(git_oid *out, git_hash_ctx *ctx) +{ + if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out->id, GIT_OID_RAWSZ, 0) < 0) + return -1; + + ctx->ctx.cng.updated = 0; + + return 0; +} + +GIT_INLINE(void) hash_cng_free(git_hash_ctx *ctx) +{ + ctx->prov->prov.cng.destroy_hash(ctx->ctx.cng.hash_handle); + git__free(ctx->ctx.cng.hash_object); +} + +/* Indirection between CryptoAPI and CNG */ + +git_hash_ctx *git_hash_ctx_new() +{ + git_global_st *global_state; + git_hash_prov *hash_prov; + + if ((global_state = git__global_state()) == NULL) + return NULL; + + hash_prov = &global_state->hash_prov; + + if (hash_prov->type == INVALID && hash_win32_prov_init(hash_prov) < 0) + return NULL; + + return (hash_prov->type == CNG) ? hash_ctx_cng_new(hash_prov) : hash_ctx_cryptoapi_new(hash_prov); +} + +int git_hash_init(git_hash_ctx *ctx) +{ + assert(ctx && ctx->type); + return (ctx->type == CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx); +} + +int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + assert(ctx && ctx->type); + return (ctx->type == CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len); +} + +int git_hash_final(git_oid *out, git_hash_ctx *ctx) +{ + assert(ctx && ctx->type); + return (ctx->type == CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx); +} + +void git_hash_ctx_free(git_hash_ctx *ctx) +{ + if (ctx == NULL) + return; + + if (ctx->type == CNG) + hash_cng_free(ctx); + else + hash_cryptoapi_free(ctx); + + git__free(ctx); +} diff --git a/src/hash/hash_win32.h b/src/hash/hash_win32.h new file mode 100644 index 000000000..b91da3e37 --- /dev/null +++ b/src/hash/hash_win32.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2009-2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef INCLUDE_hash_win32_h__ +#define INCLUDE_hash_win32_h__ + +#include "common.h" +#include "hash.h" + +#include +#include + +enum hash_win32_prov_type { + INVALID = 0, + CRYPTOAPI, + CNG +}; + +/* + * CryptoAPI is available for hashing on Windows XP and newer. + */ + +struct hash_cryptoapi_prov { + HCRYPTPROV handle; +}; + +/* + * CNG (bcrypt.dll) is significantly more performant than CryptoAPI and is + * preferred, however it is only available on Windows 2008 and newer and + * must therefore be dynamically loaded, and we must inline constants that + * would not exist when building in pre-Windows 2008 environments. + */ + +#define GIT_HASH_CNG_DLL_NAME "bcrypt.dll" + +/* BCRYPT_SHA1_ALGORITHM */ +#define GIT_HASH_CNG_HASH_TYPE L"SHA1" + +/* BCRYPT_OBJECT_LENGTH */ +#define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength" + +/* BCRYPT_HASH_REUSEABLE_FLAGS */ +#define GIT_HASH_CNG_HASH_REUSABLE 0x00000020 + +/* Function declarations for CNG */ +typedef NTSTATUS (WINAPI *hash_win32_cng_open_algorithm_provider_fn)( + HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm, + LPCWSTR pszAlgId, + LPCWSTR pszImplementation, + DWORD dwFlags); + +typedef NTSTATUS (WINAPI *hash_win32_cng_get_property_fn)( + HANDLE /* BCRYPT_HANDLE */ hObject, + LPCWSTR pszProperty, + PUCHAR pbOutput, + ULONG cbOutput, + ULONG *pcbResult, + ULONG dwFlags); + +typedef NTSTATUS (WINAPI *hash_win32_cng_create_hash_fn)( + HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm, + HANDLE /* BCRYPT_HASH_HANDLE */ *phHash, + PUCHAR pbHashObject, ULONG cbHashObject, + PUCHAR pbSecret, + ULONG cbSecret, + ULONG dwFlags); + +typedef NTSTATUS (WINAPI *hash_win32_cng_finish_hash_fn)( + HANDLE /* BCRYPT_HASH_HANDLE */ hHash, + PUCHAR pbOutput, + ULONG cbOutput, + ULONG dwFlags); + +typedef NTSTATUS (WINAPI *hash_win32_cng_hash_data_fn)( + HANDLE /* BCRYPT_HASH_HANDLE */ hHash, + PUCHAR pbInput, + ULONG cbInput, + ULONG dwFlags); + +typedef NTSTATUS (WINAPI *hash_win32_cng_destroy_hash_fn)( + HANDLE /* BCRYPT_HASH_HANDLE */ hHash); + +typedef NTSTATUS (WINAPI *hash_win32_cng_close_algorithm_provider_fn)( + HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm, + ULONG dwFlags); + +struct hash_cng_prov { + /* DLL for CNG */ + HINSTANCE dll; + + /* Function pointers for CNG */ + hash_win32_cng_open_algorithm_provider_fn open_algorithm_provider; + hash_win32_cng_get_property_fn get_property; + hash_win32_cng_create_hash_fn create_hash; + hash_win32_cng_finish_hash_fn finish_hash; + hash_win32_cng_hash_data_fn hash_data; + hash_win32_cng_destroy_hash_fn destroy_hash; + hash_win32_cng_close_algorithm_provider_fn close_algorithm_provider; + + HANDLE /* BCRYPT_ALG_HANDLE */ handle; + DWORD hash_object_size; +}; + +struct git_hash_prov { + enum hash_win32_prov_type type; + + union { + struct hash_cryptoapi_prov cryptoapi; + struct hash_cng_prov cng; + } prov; +}; + +/* Hash contexts */ + +struct hash_cryptoapi_ctx { + bool valid; + HCRYPTHASH hash_handle; +}; + +struct hash_cng_ctx { + bool updated; + HANDLE /* BCRYPT_HASH_HANDLE */ hash_handle; + PBYTE hash_object; +}; + +struct git_hash_ctx { + enum hash_win32_prov_type type; + git_hash_prov *prov; + + union { + struct hash_cryptoapi_ctx cryptoapi; + struct hash_cng_ctx cng; + } ctx; +}; + +#endif /* INCLUDE_hash_openssl_h__ */ diff --git a/src/indexer.c b/src/indexer.c index ec4ef7147..20337d552 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -17,7 +17,6 @@ #include "posix.h" #include "pack.h" #include "filebuf.h" -#include "sha1.h" #define UINT31_MAX (0x7FFFFFFF) @@ -462,7 +461,10 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress * struct entry *entry; void *packfile_hash; git_oid file_hash; - SHA_CTX ctx; + git_hash_ctx *ctx; + + ctx = git_hash_ctx_new(); + GITERR_CHECK_ALLOC(ctx); /* Test for this before resolve_deltas(), as it plays with idx->off */ if (idx->off < idx->pack->mwf.size - GIT_OID_RAWSZ) { @@ -502,12 +504,11 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress * } /* Write out the object names (SHA-1 hashes) */ - SHA1_Init(&ctx); git_vector_foreach(&idx->objects, i, entry) { git_filebuf_write(&idx->index_file, &entry->oid, sizeof(git_oid)); - SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ); + git_hash_update(ctx, &entry->oid, GIT_OID_RAWSZ); } - SHA1_Final(idx->hash.id, &ctx); + git_hash_final(&idx->hash, ctx); /* Write out the CRC32 values */ git_vector_foreach(&idx->objects, i, entry) { @@ -582,6 +583,7 @@ on_error: p_close(idx->pack->mwf.fd); git_filebuf_cleanup(&idx->index_file); git_buf_free(&filename); + git_hash_ctx_free(ctx); return -1; } @@ -682,7 +684,10 @@ int git_indexer_write(git_indexer *idx) struct entry *entry; void *packfile_hash; git_oid file_hash; - SHA_CTX ctx; + git_hash_ctx *ctx; + + ctx = git_hash_ctx_new(); + GITERR_CHECK_ALLOC(ctx); git_vector_sort(&idx->objects); @@ -712,14 +717,14 @@ int git_indexer_write(git_indexer *idx) } /* Write out the object names (SHA-1 hashes) */ - SHA1_Init(&ctx); git_vector_foreach(&idx->objects, i, entry) { - error = git_filebuf_write(&idx->file, &entry->oid, sizeof(git_oid)); - SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ); - if (error < 0) + if ((error = git_filebuf_write(&idx->file, &entry->oid, sizeof(git_oid))) < 0 || + (error = git_hash_update(ctx, &entry->oid, GIT_OID_RAWSZ)) < 0) goto cleanup; } - SHA1_Final(idx->hash.id, &ctx); + + if ((error = git_hash_final(&idx->hash, ctx)) < 0) + goto cleanup; /* Write out the CRC32 values */ git_vector_foreach(&idx->objects, i, entry) { @@ -797,6 +802,7 @@ cleanup: if (error < 0) git_filebuf_cleanup(&idx->file); git_buf_free(&filename); + git_hash_ctx_free(ctx); return error; } diff --git a/src/odb.c b/src/odb.c index d6b1de946..027aeddaa 100644 --- a/src/odb.c +++ b/src/odb.c @@ -119,21 +119,25 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) char hdr[64], buffer[2048]; git_hash_ctx *ctx; ssize_t read_len = 0; + int error = 0; if (!git_object_typeisloose(type)) { giterr_set(GITERR_INVALID, "Invalid object type for hash"); return -1; } - hdr_len = format_object_header(hdr, sizeof(hdr), size, type); - - ctx = git_hash_new_ctx(); + ctx = git_hash_ctx_new(); GITERR_CHECK_ALLOC(ctx); - git_hash_update(ctx, hdr, hdr_len); + hdr_len = format_object_header(hdr, sizeof(hdr), size, type); + + if ((error = git_hash_update(ctx, hdr, hdr_len)) < 0) + goto done; while (size > 0 && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) { - git_hash_update(ctx, buffer, read_len); + if ((error = git_hash_update(ctx, buffer, read_len)) < 0) + goto done; + size -= read_len; } @@ -141,15 +145,18 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) * If size is not zero, the file was truncated after we originally * stat'd it, so we consider this a read failure too */ if (read_len < 0 || size > 0) { - git_hash_free_ctx(ctx); giterr_set(GITERR_OS, "Error reading file for hashing"); + error = -1; + + goto done; return -1; } - git_hash_final(out, ctx); - git_hash_free_ctx(ctx); + error = git_hash_final(out, ctx); - return 0; +done: + git_hash_ctx_free(ctx); + return error; } int git_odb__hashfd_filtered( diff --git a/src/pack-objects.c b/src/pack-objects.c index 7acc93328..58a70d0e0 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -113,7 +113,7 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo) pb->repo = repo; pb->nr_threads = 1; /* do not spawn any thread by default */ - pb->ctx = git_hash_new_ctx(); + pb->ctx = git_hash_ctx_new(); if (!pb->ctx || git_repository_odb(&pb->odb, repo) < 0 || @@ -297,14 +297,13 @@ static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po) if (git_buf_put(buf, (char *)hdr, hdr_len) < 0) goto on_error; - git_hash_update(pb->ctx, hdr, hdr_len); + if (git_hash_update(pb->ctx, hdr, hdr_len) < 0) + goto on_error; if (type == GIT_OBJ_REF_DELTA) { - if (git_buf_put(buf, (char *)po->delta->id.id, - GIT_OID_RAWSZ) < 0) + if (git_buf_put(buf, (char *)po->delta->id.id, GIT_OID_RAWSZ) < 0 || + git_hash_update(pb->ctx, po->delta->id.id, GIT_OID_RAWSZ) < 0) goto on_error; - - git_hash_update(pb->ctx, po->delta->id.id, GIT_OID_RAWSZ); } /* Write data */ @@ -319,11 +318,10 @@ static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po) size = zbuf.size; } - if (git_buf_put(buf, data, size) < 0) + if (git_buf_put(buf, data, size) < 0 || + git_hash_update(pb->ctx, data, size) < 0) goto on_error; - git_hash_update(pb->ctx, data, size); - if (po->delta_data) git__free(po->delta_data); @@ -573,7 +571,8 @@ static int write_pack(git_packbuilder *pb, if (cb(&ph, sizeof(ph), data) < 0) goto on_error; - git_hash_update(pb->ctx, &ph, sizeof(ph)); + if (git_hash_update(pb->ctx, &ph, sizeof(ph)) < 0) + goto on_error; pb->nr_remaining = pb->nr_objects; do { @@ -592,7 +591,9 @@ static int write_pack(git_packbuilder *pb, git__free(write_order); git_buf_free(&buf); - git_hash_final(&pb->pack_oid, pb->ctx); + + if (git_hash_final(&pb->pack_oid, pb->ctx) < 0) + goto on_error; return cb(pb->pack_oid.id, GIT_OID_RAWSZ, data); @@ -1319,7 +1320,7 @@ void git_packbuilder_free(git_packbuilder *pb) git_odb_free(pb->odb); if (pb->ctx) - git_hash_free_ctx(pb->ctx); + git_hash_ctx_free(pb->ctx); if (pb->object_ix) git_oidmap_free(pb->object_ix); diff --git a/src/ppc/sha1.c b/src/ppc/sha1.c deleted file mode 100644 index 803b81d0a..000000000 --- a/src/ppc/sha1.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2009-2012 the libgit2 contributors - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ -#include -#include -#include "sha1.h" - -extern void ppc_sha1_core(uint32_t *hash, const unsigned char *p, - unsigned int nblocks); - -int ppc_SHA1_Init(ppc_SHA_CTX *c) -{ - c->hash[0] = 0x67452301; - c->hash[1] = 0xEFCDAB89; - c->hash[2] = 0x98BADCFE; - c->hash[3] = 0x10325476; - c->hash[4] = 0xC3D2E1F0; - c->len = 0; - c->cnt = 0; - return 0; -} - -int ppc_SHA1_Update(ppc_SHA_CTX *c, const void *ptr, unsigned long n) -{ - unsigned long nb; - const unsigned char *p = ptr; - - c->len += (uint64_t) n << 3; - while (n != 0) { - if (c->cnt || n < 64) { - nb = 64 - c->cnt; - if (nb > n) - nb = n; - memcpy(&c->buf.b[c->cnt], p, nb); - if ((c->cnt += nb) == 64) { - ppc_sha1_core(c->hash, c->buf.b, 1); - c->cnt = 0; - } - } else { - nb = n >> 6; - ppc_sha1_core(c->hash, p, nb); - nb <<= 6; - } - n -= nb; - p += nb; - } - return 0; -} - -int ppc_SHA1_Final(unsigned char *hash, ppc_SHA_CTX *c) -{ - unsigned int cnt = c->cnt; - - c->buf.b[cnt++] = 0x80; - if (cnt > 56) { - if (cnt < 64) - memset(&c->buf.b[cnt], 0, 64 - cnt); - ppc_sha1_core(c->hash, c->buf.b, 1); - cnt = 0; - } - if (cnt < 56) - memset(&c->buf.b[cnt], 0, 56 - cnt); - c->buf.l[7] = c->len; - ppc_sha1_core(c->hash, c->buf.b, 1); - memcpy(hash, c->hash, 20); - return 0; -} diff --git a/src/ppc/sha1.h b/src/ppc/sha1.h deleted file mode 100644 index aca4e5dda..000000000 --- a/src/ppc/sha1.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2009-2012 the libgit2 contributors - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ -#include - -typedef struct { - uint32_t hash[5]; - uint32_t cnt; - uint64_t len; - union { - unsigned char b[64]; - uint64_t l[8]; - } buf; -} ppc_SHA_CTX; - -int ppc_SHA1_Init(ppc_SHA_CTX *c); -int ppc_SHA1_Update(ppc_SHA_CTX *c, const void *p, unsigned long n); -int ppc_SHA1_Final(unsigned char *hash, ppc_SHA_CTX *c); - -#define SHA_CTX ppc_SHA_CTX -#define SHA1_Init ppc_SHA1_Init -#define SHA1_Update ppc_SHA1_Update -#define SHA1_Final ppc_SHA1_Final diff --git a/src/ppc/sha1ppc.S b/src/ppc/sha1ppc.S deleted file mode 100644 index 1711eef6e..000000000 --- a/src/ppc/sha1ppc.S +++ /dev/null @@ -1,224 +0,0 @@ -/* - * SHA-1 implementation for PowerPC. - * - * Copyright (C) 2005 Paul Mackerras - */ - -/* - * PowerPC calling convention: - * %r0 - volatile temp - * %r1 - stack pointer. - * %r2 - reserved - * %r3-%r12 - Incoming arguments & return values; volatile. - * %r13-%r31 - Callee-save registers - * %lr - Return address, volatile - * %ctr - volatile - * - * Register usage in this routine: - * %r0 - temp - * %r3 - argument (pointer to 5 words of SHA state) - * %r4 - argument (pointer to data to hash) - * %r5 - Constant K in SHA round (initially number of blocks to hash) - * %r6-%r10 - Working copies of SHA variables A..E (actually E..A order) - * %r11-%r26 - Data being hashed W[]. - * %r27-%r31 - Previous copies of A..E, for final add back. - * %ctr - loop count - */ - - -/* - * We roll the registers for A, B, C, D, E around on each - * iteration; E on iteration t is D on iteration t+1, and so on. - * We use registers 6 - 10 for this. (Registers 27 - 31 hold - * the previous values.) - */ -#define RA(t) (((t)+4)%5+6) -#define RB(t) (((t)+3)%5+6) -#define RC(t) (((t)+2)%5+6) -#define RD(t) (((t)+1)%5+6) -#define RE(t) (((t)+0)%5+6) - -/* We use registers 11 - 26 for the W values */ -#define W(t) ((t)%16+11) - -/* Register 5 is used for the constant k */ - -/* - * The basic SHA-1 round function is: - * E += ROTL(A,5) + F(B,C,D) + W[i] + K; B = ROTL(B,30) - * Then the variables are renamed: (A,B,C,D,E) = (E,A,B,C,D). - * - * Every 20 rounds, the function F() and the constant K changes: - * - 20 rounds of f0(b,c,d) = "bit wise b ? c : d" = (^b & d) + (b & c) - * - 20 rounds of f1(b,c,d) = b^c^d = (b^d)^c - * - 20 rounds of f2(b,c,d) = majority(b,c,d) = (b&d) + ((b^d)&c) - * - 20 more rounds of f1(b,c,d) - * - * These are all scheduled for near-optimal performance on a G4. - * The G4 is a 3-issue out-of-order machine with 3 ALUs, but it can only - * *consider* starting the oldest 3 instructions per cycle. So to get - * maximum performance out of it, you have to treat it as an in-order - * machine. Which means interleaving the computation round t with the - * computation of W[t+4]. - * - * The first 16 rounds use W values loaded directly from memory, while the - * remaining 64 use values computed from those first 16. We preload - * 4 values before starting, so there are three kinds of rounds: - * - The first 12 (all f0) also load the W values from memory. - * - The next 64 compute W(i+4) in parallel. 8*f0, 20*f1, 20*f2, 16*f1. - * - The last 4 (all f1) do not do anything with W. - * - * Therefore, we have 6 different round functions: - * STEPD0_LOAD(t,s) - Perform round t and load W(s). s < 16 - * STEPD0_UPDATE(t,s) - Perform round t and compute W(s). s >= 16. - * STEPD1_UPDATE(t,s) - * STEPD2_UPDATE(t,s) - * STEPD1(t) - Perform round t with no load or update. - * - * The G5 is more fully out-of-order, and can find the parallelism - * by itself. The big limit is that it has a 2-cycle ALU latency, so - * even though it's 2-way, the code has to be scheduled as if it's - * 4-way, which can be a limit. To help it, we try to schedule the - * read of RA(t) as late as possible so it doesn't stall waiting for - * the previous round's RE(t-1), and we try to rotate RB(t) as early - * as possible while reading RC(t) (= RB(t-1)) as late as possible. - */ - -/* the initial loads. */ -#define LOADW(s) \ - lwz W(s),(s)*4(%r4) - -/* - * Perform a step with F0, and load W(s). Uses W(s) as a temporary - * before loading it. - * This is actually 10 instructions, which is an awkward fit. - * It can execute grouped as listed, or delayed one instruction. - * (If delayed two instructions, there is a stall before the start of the - * second line.) Thus, two iterations take 7 cycles, 3.5 cycles per round. - */ -#define STEPD0_LOAD(t,s) \ -add RE(t),RE(t),W(t); andc %r0,RD(t),RB(t); and W(s),RC(t),RB(t); \ -add RE(t),RE(t),%r0; rotlwi %r0,RA(t),5; rotlwi RB(t),RB(t),30; \ -add RE(t),RE(t),W(s); add %r0,%r0,%r5; lwz W(s),(s)*4(%r4); \ -add RE(t),RE(t),%r0 - -/* - * This is likewise awkward, 13 instructions. However, it can also - * execute starting with 2 out of 3 possible moduli, so it does 2 rounds - * in 9 cycles, 4.5 cycles/round. - */ -#define STEPD0_UPDATE(t,s,loadk...) \ -add RE(t),RE(t),W(t); andc %r0,RD(t),RB(t); xor W(s),W((s)-16),W((s)-3); \ -add RE(t),RE(t),%r0; and %r0,RC(t),RB(t); xor W(s),W(s),W((s)-8); \ -add RE(t),RE(t),%r0; rotlwi %r0,RA(t),5; xor W(s),W(s),W((s)-14); \ -add RE(t),RE(t),%r5; loadk; rotlwi RB(t),RB(t),30; rotlwi W(s),W(s),1; \ -add RE(t),RE(t),%r0 - -/* Nicely optimal. Conveniently, also the most common. */ -#define STEPD1_UPDATE(t,s,loadk...) \ -add RE(t),RE(t),W(t); xor %r0,RD(t),RB(t); xor W(s),W((s)-16),W((s)-3); \ -add RE(t),RE(t),%r5; loadk; xor %r0,%r0,RC(t); xor W(s),W(s),W((s)-8); \ -add RE(t),RE(t),%r0; rotlwi %r0,RA(t),5; xor W(s),W(s),W((s)-14); \ -add RE(t),RE(t),%r0; rotlwi RB(t),RB(t),30; rotlwi W(s),W(s),1 - -/* - * The naked version, no UPDATE, for the last 4 rounds. 3 cycles per. - * We could use W(s) as a temp register, but we don't need it. - */ -#define STEPD1(t) \ - add RE(t),RE(t),W(t); xor %r0,RD(t),RB(t); \ -rotlwi RB(t),RB(t),30; add RE(t),RE(t),%r5; xor %r0,%r0,RC(t); \ -add RE(t),RE(t),%r0; rotlwi %r0,RA(t),5; /* spare slot */ \ -add RE(t),RE(t),%r0 - -/* - * 14 instructions, 5 cycles per. The majority function is a bit - * awkward to compute. This can execute with a 1-instruction delay, - * but it causes a 2-instruction delay, which triggers a stall. - */ -#define STEPD2_UPDATE(t,s,loadk...) \ -add RE(t),RE(t),W(t); and %r0,RD(t),RB(t); xor W(s),W((s)-16),W((s)-3); \ -add RE(t),RE(t),%r0; xor %r0,RD(t),RB(t); xor W(s),W(s),W((s)-8); \ -add RE(t),RE(t),%r5; loadk; and %r0,%r0,RC(t); xor W(s),W(s),W((s)-14); \ -add RE(t),RE(t),%r0; rotlwi %r0,RA(t),5; rotlwi W(s),W(s),1; \ -add RE(t),RE(t),%r0; rotlwi RB(t),RB(t),30 - -#define STEP0_LOAD4(t,s) \ - STEPD0_LOAD(t,s); \ - STEPD0_LOAD((t+1),(s)+1); \ - STEPD0_LOAD((t)+2,(s)+2); \ - STEPD0_LOAD((t)+3,(s)+3) - -#define STEPUP4(fn, t, s, loadk...) \ - STEP##fn##_UPDATE(t,s,); \ - STEP##fn##_UPDATE((t)+1,(s)+1,); \ - STEP##fn##_UPDATE((t)+2,(s)+2,); \ - STEP##fn##_UPDATE((t)+3,(s)+3,loadk) - -#define STEPUP20(fn, t, s, loadk...) \ - STEPUP4(fn, t, s,); \ - STEPUP4(fn, (t)+4, (s)+4,); \ - STEPUP4(fn, (t)+8, (s)+8,); \ - STEPUP4(fn, (t)+12, (s)+12,); \ - STEPUP4(fn, (t)+16, (s)+16, loadk) - - .globl ppc_sha1_core -ppc_sha1_core: - stwu %r1,-80(%r1) - stmw %r13,4(%r1) - - /* Load up A - E */ - lmw %r27,0(%r3) - - mtctr %r5 - -1: - LOADW(0) - lis %r5,0x5a82 - mr RE(0),%r31 - LOADW(1) - mr RD(0),%r30 - mr RC(0),%r29 - LOADW(2) - ori %r5,%r5,0x7999 /* K0-19 */ - mr RB(0),%r28 - LOADW(3) - mr RA(0),%r27 - - STEP0_LOAD4(0, 4) - STEP0_LOAD4(4, 8) - STEP0_LOAD4(8, 12) - STEPUP4(D0, 12, 16,) - STEPUP4(D0, 16, 20, lis %r5,0x6ed9) - - ori %r5,%r5,0xeba1 /* K20-39 */ - STEPUP20(D1, 20, 24, lis %r5,0x8f1b) - - ori %r5,%r5,0xbcdc /* K40-59 */ - STEPUP20(D2, 40, 44, lis %r5,0xca62) - - ori %r5,%r5,0xc1d6 /* K60-79 */ - STEPUP4(D1, 60, 64,) - STEPUP4(D1, 64, 68,) - STEPUP4(D1, 68, 72,) - STEPUP4(D1, 72, 76,) - addi %r4,%r4,64 - STEPD1(76) - STEPD1(77) - STEPD1(78) - STEPD1(79) - - /* Add results to original values */ - add %r31,%r31,RE(0) - add %r30,%r30,RD(0) - add %r29,%r29,RC(0) - add %r28,%r28,RB(0) - add %r27,%r27,RA(0) - - bdnz 1b - - /* Save final hash, restore registers, and return */ - stmw %r27,0(%r3) - lmw %r13,4(%r1) - addi %r1,%r1,80 - blr diff --git a/src/sha1/sha1.c b/src/sha1/sha1.c deleted file mode 100644 index 8aaedeb8f..000000000 --- a/src/sha1/sha1.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (C) 2009-2012 the libgit2 contributors - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#include "common.h" -#include "sha1.h" - -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) - -/* - * Force usage of rol or ror by selecting the one with the smaller constant. - * It _can_ generate slightly smaller code (a constant of 1 is special), but - * perhaps more importantly it's possibly faster on any uarch that does a - * rotate with a loop. - */ - -#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }) -#define SHA_ROL(x,n) SHA_ASM("rol", x, n) -#define SHA_ROR(x,n) SHA_ASM("ror", x, n) - -#else - -#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r))) -#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n)) -#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n) - -#endif - -/* - * If you have 32 registers or more, the compiler can (and should) - * try to change the array[] accesses into registers. However, on - * machines with less than ~25 registers, that won't really work, - * and at least gcc will make an unholy mess of it. - * - * So to avoid that mess which just slows things down, we force - * the stores to memory to actually happen (we might be better off - * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as - * suggested by Artur Skawina - that will also make gcc unable to - * try to do the silly "optimize away loads" part because it won't - * see what the value will be). - * - * Ben Herrenschmidt reports that on PPC, the C version comes close - * to the optimized asm with this (ie on PPC you don't want that - * 'volatile', since there are lots of registers). - * - * On ARM we get the best code generation by forcing a full memory barrier - * between each SHA_ROUND, otherwise gcc happily get wild with spilling and - * the stack frame size simply explode and performance goes down the drain. - */ - -#if defined(__i386__) || defined(__x86_64__) - #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val)) -#elif defined(__GNUC__) && defined(__arm__) - #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0) -#else - #define setW(x, val) (W(x) = (val)) -#endif - -/* - * Performance might be improved if the CPU architecture is OK with - * unaligned 32-bit loads and a fast ntohl() is available. - * Otherwise fall back to byte loads and shifts which is portable, - * and is faster on architectures with memory alignment issues. - */ - -#if defined(__i386__) || defined(__x86_64__) || \ - defined(_M_IX86) || defined(_M_X64) || \ - defined(__ppc__) || defined(__ppc64__) || \ - defined(__powerpc__) || defined(__powerpc64__) || \ - defined(__s390__) || defined(__s390x__) - -#define get_be32(p) ntohl(*(const unsigned int *)(p)) -#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) - -#else - -#define get_be32(p) ( \ - (*((const unsigned char *)(p) + 0) << 24) | \ - (*((const unsigned char *)(p) + 1) << 16) | \ - (*((const unsigned char *)(p) + 2) << 8) | \ - (*((const unsigned char *)(p) + 3) << 0) ) -#define put_be32(p, v) do { \ - unsigned int __v = (v); \ - *((unsigned char *)(p) + 0) = __v >> 24; \ - *((unsigned char *)(p) + 1) = __v >> 16; \ - *((unsigned char *)(p) + 2) = __v >> 8; \ - *((unsigned char *)(p) + 3) = __v >> 0; } while (0) - -#endif - -/* This "rolls" over the 512-bit array */ -#define W(x) (array[(x)&15]) - -/* - * Where do we get the source from? The first 16 iterations get it from - * the input data, the next mix it from the 512-bit array. - */ -#define SHA_SRC(t) get_be32(data + t) -#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) - -#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ - unsigned int TEMP = input(t); setW(t, TEMP); \ - E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \ - B = SHA_ROR(B, 2); } while (0) - -#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) -#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) -#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E ) -#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) -#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E ) - -static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data) -{ - unsigned int A,B,C,D,E; - unsigned int array[16]; - - A = ctx->H[0]; - B = ctx->H[1]; - C = ctx->H[2]; - D = ctx->H[3]; - E = ctx->H[4]; - - /* Round 1 - iterations 0-16 take their input from 'data' */ - T_0_15( 0, A, B, C, D, E); - T_0_15( 1, E, A, B, C, D); - T_0_15( 2, D, E, A, B, C); - T_0_15( 3, C, D, E, A, B); - T_0_15( 4, B, C, D, E, A); - T_0_15( 5, A, B, C, D, E); - T_0_15( 6, E, A, B, C, D); - T_0_15( 7, D, E, A, B, C); - T_0_15( 8, C, D, E, A, B); - T_0_15( 9, B, C, D, E, A); - T_0_15(10, A, B, C, D, E); - T_0_15(11, E, A, B, C, D); - T_0_15(12, D, E, A, B, C); - T_0_15(13, C, D, E, A, B); - T_0_15(14, B, C, D, E, A); - T_0_15(15, A, B, C, D, E); - - /* Round 1 - tail. Input from 512-bit mixing array */ - T_16_19(16, E, A, B, C, D); - T_16_19(17, D, E, A, B, C); - T_16_19(18, C, D, E, A, B); - T_16_19(19, B, C, D, E, A); - - /* Round 2 */ - T_20_39(20, A, B, C, D, E); - T_20_39(21, E, A, B, C, D); - T_20_39(22, D, E, A, B, C); - T_20_39(23, C, D, E, A, B); - T_20_39(24, B, C, D, E, A); - T_20_39(25, A, B, C, D, E); - T_20_39(26, E, A, B, C, D); - T_20_39(27, D, E, A, B, C); - T_20_39(28, C, D, E, A, B); - T_20_39(29, B, C, D, E, A); - T_20_39(30, A, B, C, D, E); - T_20_39(31, E, A, B, C, D); - T_20_39(32, D, E, A, B, C); - T_20_39(33, C, D, E, A, B); - T_20_39(34, B, C, D, E, A); - T_20_39(35, A, B, C, D, E); - T_20_39(36, E, A, B, C, D); - T_20_39(37, D, E, A, B, C); - T_20_39(38, C, D, E, A, B); - T_20_39(39, B, C, D, E, A); - - /* Round 3 */ - T_40_59(40, A, B, C, D, E); - T_40_59(41, E, A, B, C, D); - T_40_59(42, D, E, A, B, C); - T_40_59(43, C, D, E, A, B); - T_40_59(44, B, C, D, E, A); - T_40_59(45, A, B, C, D, E); - T_40_59(46, E, A, B, C, D); - T_40_59(47, D, E, A, B, C); - T_40_59(48, C, D, E, A, B); - T_40_59(49, B, C, D, E, A); - T_40_59(50, A, B, C, D, E); - T_40_59(51, E, A, B, C, D); - T_40_59(52, D, E, A, B, C); - T_40_59(53, C, D, E, A, B); - T_40_59(54, B, C, D, E, A); - T_40_59(55, A, B, C, D, E); - T_40_59(56, E, A, B, C, D); - T_40_59(57, D, E, A, B, C); - T_40_59(58, C, D, E, A, B); - T_40_59(59, B, C, D, E, A); - - /* Round 4 */ - T_60_79(60, A, B, C, D, E); - T_60_79(61, E, A, B, C, D); - T_60_79(62, D, E, A, B, C); - T_60_79(63, C, D, E, A, B); - T_60_79(64, B, C, D, E, A); - T_60_79(65, A, B, C, D, E); - T_60_79(66, E, A, B, C, D); - T_60_79(67, D, E, A, B, C); - T_60_79(68, C, D, E, A, B); - T_60_79(69, B, C, D, E, A); - T_60_79(70, A, B, C, D, E); - T_60_79(71, E, A, B, C, D); - T_60_79(72, D, E, A, B, C); - T_60_79(73, C, D, E, A, B); - T_60_79(74, B, C, D, E, A); - T_60_79(75, A, B, C, D, E); - T_60_79(76, E, A, B, C, D); - T_60_79(77, D, E, A, B, C); - T_60_79(78, C, D, E, A, B); - T_60_79(79, B, C, D, E, A); - - ctx->H[0] += A; - ctx->H[1] += B; - ctx->H[2] += C; - ctx->H[3] += D; - ctx->H[4] += E; -} - -void git__blk_SHA1_Init(blk_SHA_CTX *ctx) -{ - ctx->size = 0; - - /* Initialize H with the magic constants (see FIPS180 for constants) */ - ctx->H[0] = 0x67452301; - ctx->H[1] = 0xefcdab89; - ctx->H[2] = 0x98badcfe; - ctx->H[3] = 0x10325476; - ctx->H[4] = 0xc3d2e1f0; -} - -void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, size_t len) -{ - unsigned int lenW = ctx->size & 63; - - ctx->size += len; - - /* Read the data into W and process blocks as they get full */ - if (lenW) { - unsigned int left = 64 - lenW; - if (len < left) - left = (unsigned int)len; - memcpy(lenW + (char *)ctx->W, data, left); - lenW = (lenW + left) & 63; - len -= left; - data = ((const char *)data + left); - if (lenW) - return; - blk_SHA1_Block(ctx, ctx->W); - } - while (len >= 64) { - blk_SHA1_Block(ctx, data); - data = ((const char *)data + 64); - len -= 64; - } - if (len) - memcpy(ctx->W, data, len); -} - -void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) -{ - static const unsigned char pad[64] = { 0x80 }; - unsigned int padlen[2]; - int i; - - /* Pad with a binary 1 (ie 0x80), then zeroes, then length */ - padlen[0] = htonl((uint32_t)(ctx->size >> 29)); - padlen[1] = htonl((uint32_t)(ctx->size << 3)); - - i = ctx->size & 63; - git__blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i))); - git__blk_SHA1_Update(ctx, padlen, 8); - - /* Output hash */ - for (i = 0; i < 5; i++) - put_be32(hashout + i*4, ctx->H[i]); -} diff --git a/tests-clar/object/raw/hash.c b/tests-clar/object/raw/hash.c index 4b8b1b74c..94be42cdc 100644 --- a/tests-clar/object/raw/hash.c +++ b/tests-clar/object/raw/hash.c @@ -26,22 +26,22 @@ void test_object_raw_hash__hash_by_blocks(void) git_hash_ctx *ctx; git_oid id1, id2; - cl_assert((ctx = git_hash_new_ctx()) != NULL); + cl_assert((ctx = git_hash_ctx_new()) != NULL); /* should already be init'd */ - git_hash_update(ctx, hello_text, strlen(hello_text)); - git_hash_final(&id2, ctx); + cl_git_pass(git_hash_update(ctx, hello_text, strlen(hello_text))); + cl_git_pass(git_hash_final(&id2, ctx)); cl_git_pass(git_oid_fromstr(&id1, hello_id)); cl_assert(git_oid_cmp(&id1, &id2) == 0); /* reinit should permit reuse */ - git_hash_init(ctx); - git_hash_update(ctx, bye_text, strlen(bye_text)); - git_hash_final(&id2, ctx); + cl_git_pass(git_hash_init(ctx)); + cl_git_pass(git_hash_update(ctx, bye_text, strlen(bye_text))); + cl_git_pass(git_hash_final(&id2, ctx)); cl_git_pass(git_oid_fromstr(&id1, bye_id)); cl_assert(git_oid_cmp(&id1, &id2) == 0); - git_hash_free_ctx(ctx); + git_hash_ctx_free(ctx); } void test_object_raw_hash__hash_buffer_in_single_call(void) -- cgit v1.2.1