]> git.proxmox.com Git - libgit2.git/blame - src/hash/hash_win32.c
Merge pull request #2673 from swisspol/2672
[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
a3aa5f4d
RB
92static void git_hash_global_shutdown(void)
93{
94 if (hash_prov.type == CNG)
95 hash_cng_prov_shutdown();
96 else if(hash_prov.type == CRYPTOAPI)
97 hash_cryptoapi_prov_shutdown();
98}
99
100int git_hash_global_init(void)
d6fb0924
ET
101{
102 int error = 0;
103
7ebefd22
ET
104 if (hash_prov.type != INVALID)
105 return 0;
d6fb0924 106
7ebefd22
ET
107 if ((error = hash_cng_prov_init()) < 0)
108 error = hash_cryptoapi_prov_init();
d6fb0924 109
a3aa5f4d 110 git__on_shutdown(git_hash_global_shutdown);
d6fb0924 111
a3aa5f4d 112 return error;
a8527429
ET
113}
114
d6fb0924
ET
115/* CryptoAPI: available in Windows XP and newer */
116
7ebefd22 117GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_ctx *ctx)
d6fb0924 118{
d6fb0924 119 ctx->type = CRYPTOAPI;
7ebefd22 120 ctx->prov = &hash_prov;
d6fb0924 121
8005c6d4 122 return git_hash_init(ctx);
d6fb0924
ET
123}
124
125GIT_INLINE(int) hash_cryptoapi_init(git_hash_ctx *ctx)
126{
127 if (ctx->ctx.cryptoapi.valid)
128 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
129
130 if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
131 ctx->ctx.cryptoapi.valid = 0;
132 return -1;
133 }
134
135 ctx->ctx.cryptoapi.valid = 1;
136 return 0;
137}
138
139GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *data, size_t len)
140{
141 assert(ctx->ctx.cryptoapi.valid);
142
a8122b5d 143 if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, (const BYTE *)data, (DWORD)len, 0))
d6fb0924
ET
144 return -1;
145
146 return 0;
147}
148
149GIT_INLINE(int) hash_cryptoapi_final(git_oid *out, git_hash_ctx *ctx)
150{
151 DWORD len = 20;
152 int error = 0;
153
154 assert(ctx->ctx.cryptoapi.valid);
155
156 if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out->id, &len, 0))
157 error = -1;
158
159 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
160 ctx->ctx.cryptoapi.valid = 0;
161
162 return error;
163}
164
603bee07 165GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_ctx *ctx)
d6fb0924
ET
166{
167 if (ctx->ctx.cryptoapi.valid)
168 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
169}
170
171/* CNG: Available in Windows Server 2008 and newer */
172
7ebefd22 173GIT_INLINE(int) hash_ctx_cng_init(git_hash_ctx *ctx)
d6fb0924 174{
7ebefd22 175 if ((ctx->ctx.cng.hash_object = git__malloc(hash_prov.prov.cng.hash_object_size)) == NULL)
603bee07 176 return -1;
d6fb0924 177
7ebefd22 178 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 179 git__free(ctx->ctx.cng.hash_object);
603bee07 180 return -1;
d6fb0924
ET
181 }
182
183 ctx->type = CNG;
7ebefd22 184 ctx->prov = &hash_prov;
d6fb0924 185
603bee07 186 return 0;
d6fb0924
ET
187}
188
189GIT_INLINE(int) hash_cng_init(git_hash_ctx *ctx)
190{
191 BYTE hash[GIT_OID_RAWSZ];
192
193 if (!ctx->ctx.cng.updated)
194 return 0;
195
196 /* CNG needs to be finished to restart */
197 if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0)
198 return -1;
199
200 ctx->ctx.cng.updated = 0;
201
202 return 0;
203}
204
205GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *data, size_t len)
206{
a8122b5d 207 if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, (PBYTE)data, (ULONG)len, 0) < 0)
d6fb0924
ET
208 return -1;
209
210 return 0;
211}
212
213GIT_INLINE(int) hash_cng_final(git_oid *out, git_hash_ctx *ctx)
214{
215 if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out->id, GIT_OID_RAWSZ, 0) < 0)
216 return -1;
217
218 ctx->ctx.cng.updated = 0;
219
220 return 0;
221}
222
603bee07 223GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_ctx *ctx)
d6fb0924
ET
224{
225 ctx->prov->prov.cng.destroy_hash(ctx->ctx.cng.hash_handle);
226 git__free(ctx->ctx.cng.hash_object);
227}
228
229/* Indirection between CryptoAPI and CNG */
230
603bee07 231int git_hash_ctx_init(git_hash_ctx *ctx)
d6fb0924 232{
7ebefd22 233 int error = 0;
d6fb0924 234
7ebefd22 235 assert(ctx);
d6fb0924 236
7ebefd22
ET
237 /*
238 * When compiled with GIT_THREADS, the global hash_prov data is
239 * initialized with git_threads_init. Otherwise, it must be initialized
240 * at first use.
241 */
242 if (hash_prov.type == INVALID && (error = git_hash_global_init()) < 0)
243 return error;
d6fb0924 244
7ebefd22 245 memset(ctx, 0x0, sizeof(git_hash_ctx));
d6fb0924 246
7ebefd22 247 return (hash_prov.type == CNG) ? hash_ctx_cng_init(ctx) : hash_ctx_cryptoapi_init(ctx);
d6fb0924
ET
248}
249
8005c6d4 250int git_hash_init(git_hash_ctx *ctx)
d6fb0924
ET
251{
252 assert(ctx && ctx->type);
253 return (ctx->type == CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx);
254}
255
256int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
257{
258 assert(ctx && ctx->type);
259 return (ctx->type == CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len);
260}
261
262int git_hash_final(git_oid *out, git_hash_ctx *ctx)
263{
264 assert(ctx && ctx->type);
265 return (ctx->type == CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx);
266}
267
603bee07 268void git_hash_ctx_cleanup(git_hash_ctx *ctx)
d6fb0924 269{
603bee07 270 assert(ctx);
d6fb0924
ET
271
272 if (ctx->type == CNG)
603bee07
ET
273 hash_ctx_cng_cleanup(ctx);
274 else if(ctx->type == CRYPTOAPI)
275 hash_ctx_cryptoapi_cleanup(ctx);
d6fb0924 276}