summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt13
-rw-r--r--src/filebuf.c6
-rw-r--r--src/global.h6
-rw-r--r--src/hash.c75
-rw-r--r--src/hash.h25
-rw-r--r--src/hash/hash_generic.c (renamed from src/sha1/sha1.c)47
-rw-r--r--src/hash/hash_generic.h19
-rw-r--r--src/hash/hash_openssl.h58
-rw-r--r--src/hash/hash_ppc.c (renamed from src/ppc/sha1.c)41
-rw-r--r--src/hash/hash_ppc.h (renamed from src/ppc/sha1.h)17
-rw-r--r--src/hash/hash_ppc_core.S (renamed from src/ppc/sha1ppc.S)4
-rw-r--r--src/hash/hash_win32.c276
-rw-r--r--src/hash/hash_win32.h140
-rw-r--r--src/indexer.c28
-rw-r--r--src/odb.c25
-rw-r--r--src/pack-objects.c25
-rw-r--r--tests-clar/object/raw/hash.c14
17 files changed, 681 insertions, 138 deletions
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/sha1/sha1.c b/src/hash/hash_generic.c
index 8aaedeb8f..cab5469d7 100644
--- a/src/sha1/sha1.c
+++ b/src/hash/hash_generic.c
@@ -6,7 +6,8 @@
*/
#include "common.h"
-#include "sha1.h"
+#include "hash.h"
+#include "hash/hash_generic.h"
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
@@ -112,7 +113,7 @@
#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)
+static void hash__block(git_hash_ctx *ctx, const unsigned int *data)
{
unsigned int A,B,C,D,E;
unsigned int array[16];
@@ -220,7 +221,19 @@ static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data)
ctx->H[4] += E;
}
-void git__blk_SHA1_Init(blk_SHA_CTX *ctx)
+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;
@@ -230,9 +243,11 @@ void git__blk_SHA1_Init(blk_SHA_CTX *ctx)
ctx->H[2] = 0x98badcfe;
ctx->H[3] = 0x10325476;
ctx->H[4] = 0xc3d2e1f0;
+
+ return 0;
}
-void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, size_t len)
+int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
{
unsigned int lenW = ctx->size & 63;
@@ -248,19 +263,21 @@ void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, size_t len)
len -= left;
data = ((const char *)data + left);
if (lenW)
- return;
- blk_SHA1_Block(ctx, ctx->W);
+ return 0;
+ hash__block(ctx, ctx->W);
}
while (len >= 64) {
- blk_SHA1_Block(ctx, data);
+ hash__block(ctx, data);
data = ((const char *)data + 64);
len -= 64;
}
if (len)
memcpy(ctx->W, data, len);
+
+ return 0;
}
-void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
+int git_hash_final(git_oid *out, git_hash_ctx *ctx)
{
static const unsigned char pad[64] = { 0x80 };
unsigned int padlen[2];
@@ -271,10 +288,18 @@ void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
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);
+ 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(hashout + i*4, ctx->H[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 <openssl/sha.h>
+
+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/ppc/sha1.c b/src/hash/hash_ppc.c
index 803b81d0a..95ad3b1a1 100644
--- a/src/ppc/sha1.c
+++ b/src/hash/hash_ppc.c
@@ -4,14 +4,29 @@
* 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 <stdio.h>
#include <string.h>
-#include "sha1.h"
-extern void ppc_sha1_core(uint32_t *hash, const unsigned char *p,
+#include "common.h"
+#include "hash.h"
+
+extern void hash_ppc_core(uint32_t *hash, const unsigned char *p,
unsigned int nblocks);
-int ppc_SHA1_Init(ppc_SHA_CTX *c)
+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;
@@ -23,7 +38,7 @@ int ppc_SHA1_Init(ppc_SHA_CTX *c)
return 0;
}
-int ppc_SHA1_Update(ppc_SHA_CTX *c, const void *ptr, unsigned long n)
+int git_hash_update(git_hash_ctx *c, const void *ptr, size_t n)
{
unsigned long nb;
const unsigned char *p = ptr;
@@ -36,12 +51,12 @@ int ppc_SHA1_Update(ppc_SHA_CTX *c, const void *ptr, unsigned long 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);
+ hash_ppc_core(c->hash, c->buf.b, 1);
c->cnt = 0;
}
} else {
nb = n >> 6;
- ppc_sha1_core(c->hash, p, nb);
+ hash_ppc_core(c->hash, p, nb);
nb <<= 6;
}
n -= nb;
@@ -50,7 +65,7 @@ int ppc_SHA1_Update(ppc_SHA_CTX *c, const void *ptr, unsigned long n)
return 0;
}
-int ppc_SHA1_Final(unsigned char *hash, ppc_SHA_CTX *c)
+int git_hash_final(git_oid *oid, git_hash_ctx *c)
{
unsigned int cnt = c->cnt;
@@ -58,13 +73,19 @@ int ppc_SHA1_Final(unsigned char *hash, ppc_SHA_CTX *c)
if (cnt > 56) {
if (cnt < 64)
memset(&c->buf.b[cnt], 0, 64 - cnt);
- ppc_sha1_core(c->hash, c->buf.b, 1);
+ 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;
- ppc_sha1_core(c->hash, c->buf.b, 1);
- memcpy(hash, c->hash, 20);
+ 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/ppc/sha1.h b/src/hash/hash_ppc.h
index aca4e5dda..200d19310 100644
--- a/src/ppc/sha1.h
+++ b/src/hash/hash_ppc.h
@@ -4,9 +4,13 @@
* 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 <stdint.h>
-typedef struct {
+struct git_hash_ctx {
uint32_t hash[5];
uint32_t cnt;
uint64_t len;
@@ -14,13 +18,6 @@ typedef struct {
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
+#endif /* INCLUDE_hash_generic_h__ */
diff --git a/src/ppc/sha1ppc.S b/src/hash/hash_ppc_core.S
index 1711eef6e..1de816cf5 100644
--- a/src/ppc/sha1ppc.S
+++ b/src/hash/hash_ppc_core.S
@@ -162,8 +162,8 @@ add RE(t),RE(t),%r0; rotlwi RB(t),RB(t),30
STEPUP4(fn, (t)+12, (s)+12,); \
STEPUP4(fn, (t)+16, (s)+16, loadk)
- .globl ppc_sha1_core
-ppc_sha1_core:
+ .globl hash_ppc_core
+hash_ppc_core:
stwu %r1,-80(%r1)
stmw %r13,4(%r1)
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 <wincrypt.h>
+#include <strsafe.h>
+
+/* 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 <wincrypt.h>
+#include <strsafe.h>
+
+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/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)