]> git.proxmox.com Git - libgit2.git/blame - src/hash/hash_win32.c
Fix longstanding valgrind warning
[libgit2.git] / src / hash / hash_win32.c
CommitLineData
d6fb0924 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
d6fb0924
ET
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8#include "common.h"
9#include "global.h"
10#include "hash.h"
11#include "hash/hash_win32.h"
12
13#include <wincrypt.h>
14#include <strsafe.h>
15
7ebefd22
ET
16static struct git_hash_prov hash_prov = {0};
17
18/* Hash initialization */
19
d6fb0924 20/* Initialize CNG, if available */
7ebefd22 21GIT_INLINE(int) hash_cng_prov_init(void)
d6fb0924 22{
d6fb0924
ET
23 char dll_path[MAX_PATH];
24 DWORD dll_path_len, size_len;
25
26 /* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
1ff3a094 27 if (!git_has_win32_version(6, 0, 1))
d6fb0924
ET
28 return -1;
29
30 /* Load bcrypt.dll explicitly from the system directory */
43095341
RB
31 if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 ||
32 dll_path_len > MAX_PATH ||
d6fb0924
ET
33 StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
34 StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
7ebefd22 35 (hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL)
d6fb0924
ET
36 return -1;
37
38 /* Load the function addresses */
7ebefd22
ET
39 if ((hash_prov.prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL ||
40 (hash_prov.prov.cng.get_property = (hash_win32_cng_get_property_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptGetProperty")) == NULL ||
41 (hash_prov.prov.cng.create_hash = (hash_win32_cng_create_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCreateHash")) == NULL ||
42 (hash_prov.prov.cng.finish_hash = (hash_win32_cng_finish_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptFinishHash")) == NULL ||
43 (hash_prov.prov.cng.hash_data = (hash_win32_cng_hash_data_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptHashData")) == NULL ||
44 (hash_prov.prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptDestroyHash")) == NULL ||
45 (hash_prov.prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) {
46 FreeLibrary(hash_prov.prov.cng.dll);
d6fb0924
ET
47 return -1;
48 }
49
50 /* Load the SHA1 algorithm */
7ebefd22
ET
51 if (hash_prov.prov.cng.open_algorithm_provider(&hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) {
52 FreeLibrary(hash_prov.prov.cng.dll);
d6fb0924
ET
53 return -1;
54 }
55
56 /* Get storage space for the hash object */
7ebefd22
ET
57 if (hash_prov.prov.cng.get_property(hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_prov.prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) {
58 hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
59 FreeLibrary(hash_prov.prov.cng.dll);
d6fb0924
ET
60 return -1;
61 }
62
7ebefd22 63 hash_prov.type = CNG;
d6fb0924
ET
64 return 0;
65}
66
a8527429
ET
67GIT_INLINE(void) hash_cng_prov_shutdown(void)
68{
69 hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
70 FreeLibrary(hash_prov.prov.cng.dll);
71
72 hash_prov.type = INVALID;
73}
74
d6fb0924 75/* Initialize CryptoAPI */
7ebefd22 76GIT_INLINE(int) hash_cryptoapi_prov_init()
d6fb0924 77{
7ebefd22 78 if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
d6fb0924
ET
79 return -1;
80
7ebefd22 81 hash_prov.type = CRYPTOAPI;
d6fb0924
ET
82 return 0;
83}
84
a8527429
ET
85GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
86{
87 CryptReleaseContext(hash_prov.prov.cryptoapi.handle, 0);
88
89 hash_prov.type = INVALID;
90}
91
7ebefd22 92int git_hash_global_init()
d6fb0924
ET
93{
94 int error = 0;
95
7ebefd22
ET
96 if (hash_prov.type != INVALID)
97 return 0;
d6fb0924 98
7ebefd22
ET
99 if ((error = hash_cng_prov_init()) < 0)
100 error = hash_cryptoapi_prov_init();
d6fb0924 101
7ebefd22 102 return error;
d6fb0924
ET
103}
104
a8527429
ET
105void git_hash_global_shutdown()
106{
107 if (hash_prov.type == CNG)
108 hash_cng_prov_shutdown();
109 else if(hash_prov.type == CRYPTOAPI)
110 hash_cryptoapi_prov_shutdown();
111}
112
d6fb0924
ET
113/* CryptoAPI: available in Windows XP and newer */
114
7ebefd22 115GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_ctx *ctx)
d6fb0924 116{
d6fb0924 117 ctx->type = CRYPTOAPI;
7ebefd22 118 ctx->prov = &hash_prov;
d6fb0924 119
8005c6d4 120 return git_hash_init(ctx);
d6fb0924
ET
121}
122
123GIT_INLINE(int) hash_cryptoapi_init(git_hash_ctx *ctx)
124{
125 if (ctx->ctx.cryptoapi.valid)
126 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
127
128 if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
129 ctx->ctx.cryptoapi.valid = 0;
130 return -1;
131 }
132
133 ctx->ctx.cryptoapi.valid = 1;
134 return 0;
135}
136
137GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *data, size_t len)
138{
139 assert(ctx->ctx.cryptoapi.valid);
140
a8122b5d 141 if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, (const BYTE *)data, (DWORD)len, 0))
d6fb0924
ET
142 return -1;
143
144 return 0;
145}
146
147GIT_INLINE(int) hash_cryptoapi_final(git_oid *out, git_hash_ctx *ctx)
148{
149 DWORD len = 20;
150 int error = 0;
151
152 assert(ctx->ctx.cryptoapi.valid);
153
154 if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out->id, &len, 0))
155 error = -1;
156
157 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
158 ctx->ctx.cryptoapi.valid = 0;
159
160 return error;
161}
162
603bee07 163GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_ctx *ctx)
d6fb0924
ET
164{
165 if (ctx->ctx.cryptoapi.valid)
166 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
167}
168
169/* CNG: Available in Windows Server 2008 and newer */
170
7ebefd22 171GIT_INLINE(int) hash_ctx_cng_init(git_hash_ctx *ctx)
d6fb0924 172{
7ebefd22 173 if ((ctx->ctx.cng.hash_object = git__malloc(hash_prov.prov.cng.hash_object_size)) == NULL)
603bee07 174 return -1;
d6fb0924 175
7ebefd22 176 if (hash_prov.prov.cng.create_hash(hash_prov.prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_prov.prov.cng.hash_object_size, NULL, 0, 0) < 0) {
d6fb0924 177 git__free(ctx->ctx.cng.hash_object);
603bee07 178 return -1;
d6fb0924
ET
179 }
180
181 ctx->type = CNG;
7ebefd22 182 ctx->prov = &hash_prov;
d6fb0924 183
603bee07 184 return 0;
d6fb0924
ET
185}
186
187GIT_INLINE(int) hash_cng_init(git_hash_ctx *ctx)
188{
189 BYTE hash[GIT_OID_RAWSZ];
190
191 if (!ctx->ctx.cng.updated)
192 return 0;
193
194 /* CNG needs to be finished to restart */
195 if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0)
196 return -1;
197
198 ctx->ctx.cng.updated = 0;
199
200 return 0;
201}
202
203GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *data, size_t len)
204{
a8122b5d 205 if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, (PBYTE)data, (ULONG)len, 0) < 0)
d6fb0924
ET
206 return -1;
207
208 return 0;
209}
210
211GIT_INLINE(int) hash_cng_final(git_oid *out, git_hash_ctx *ctx)
212{
213 if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out->id, GIT_OID_RAWSZ, 0) < 0)
214 return -1;
215
216 ctx->ctx.cng.updated = 0;
217
218 return 0;
219}
220
603bee07 221GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_ctx *ctx)
d6fb0924
ET
222{
223 ctx->prov->prov.cng.destroy_hash(ctx->ctx.cng.hash_handle);
224 git__free(ctx->ctx.cng.hash_object);
225}
226
227/* Indirection between CryptoAPI and CNG */
228
603bee07 229int git_hash_ctx_init(git_hash_ctx *ctx)
d6fb0924 230{
7ebefd22 231 int error = 0;
d6fb0924 232
7ebefd22 233 assert(ctx);
d6fb0924 234
7ebefd22
ET
235 /*
236 * When compiled with GIT_THREADS, the global hash_prov data is
237 * initialized with git_threads_init. Otherwise, it must be initialized
238 * at first use.
239 */
240 if (hash_prov.type == INVALID && (error = git_hash_global_init()) < 0)
241 return error;
d6fb0924 242
7ebefd22 243 memset(ctx, 0x0, sizeof(git_hash_ctx));
d6fb0924 244
7ebefd22 245 return (hash_prov.type == CNG) ? hash_ctx_cng_init(ctx) : hash_ctx_cryptoapi_init(ctx);
d6fb0924
ET
246}
247
8005c6d4 248int git_hash_init(git_hash_ctx *ctx)
d6fb0924
ET
249{
250 assert(ctx && ctx->type);
251 return (ctx->type == CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx);
252}
253
254int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
255{
256 assert(ctx && ctx->type);
257 return (ctx->type == CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len);
258}
259
260int git_hash_final(git_oid *out, git_hash_ctx *ctx)
261{
262 assert(ctx && ctx->type);
263 return (ctx->type == CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx);
264}
265
603bee07 266void git_hash_ctx_cleanup(git_hash_ctx *ctx)
d6fb0924 267{
603bee07 268 assert(ctx);
d6fb0924
ET
269
270 if (ctx->type == CNG)
603bee07
ET
271 hash_ctx_cng_cleanup(ctx);
272 else if(ctx->type == CRYPTOAPI)
273 hash_ctx_cryptoapi_cleanup(ctx);
d6fb0924 274}