]> git.proxmox.com Git - libgit2.git/blob - src/util/hash/win32.c
New upstream version 1.5.0+ds
[libgit2.git] / src / util / hash / win32.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
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 "win32.h"
9
10 #include "runtime.h"
11
12 #include <wincrypt.h>
13 #include <strsafe.h>
14
15 #define GIT_HASH_CNG_DLL_NAME "bcrypt.dll"
16
17 /* BCRYPT_SHA1_ALGORITHM */
18 #define GIT_HASH_CNG_SHA1_TYPE L"SHA1"
19 #define GIT_HASH_CNG_SHA256_TYPE L"SHA256"
20
21 /* BCRYPT_OBJECT_LENGTH */
22 #define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength"
23
24 /* BCRYPT_HASH_REUSEABLE_FLAGS */
25 #define GIT_HASH_CNG_HASH_REUSABLE 0x00000020
26
27 /* Definitions */
28
29 /* CryptoAPI is available for hashing on Windows XP and newer. */
30 struct cryptoapi_provider {
31 HCRYPTPROV handle;
32 };
33
34 /*
35 * CNG (bcrypt.dll) is significantly more performant than CryptoAPI and is
36 * preferred, however it is only available on Windows 2008 and newer and
37 * must therefore be dynamically loaded, and we must inline constants that
38 * would not exist when building in pre-Windows 2008 environments.
39 */
40
41 /* Function declarations for CNG */
42 typedef NTSTATUS (WINAPI *cng_open_algorithm_provider_fn)(
43 HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm,
44 LPCWSTR pszAlgId,
45 LPCWSTR pszImplementation,
46 DWORD dwFlags);
47
48 typedef NTSTATUS (WINAPI *cng_get_property_fn)(
49 HANDLE /* BCRYPT_HANDLE */ hObject,
50 LPCWSTR pszProperty,
51 PUCHAR pbOutput,
52 ULONG cbOutput,
53 ULONG *pcbResult,
54 ULONG dwFlags);
55
56 typedef NTSTATUS (WINAPI *cng_create_hash_fn)(
57 HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
58 HANDLE /* BCRYPT_HASH_HANDLE */ *phHash,
59 PUCHAR pbHashObject, ULONG cbHashObject,
60 PUCHAR pbSecret,
61 ULONG cbSecret,
62 ULONG dwFlags);
63
64 typedef NTSTATUS (WINAPI *cng_finish_hash_fn)(
65 HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
66 PUCHAR pbOutput,
67 ULONG cbOutput,
68 ULONG dwFlags);
69
70 typedef NTSTATUS (WINAPI *cng_hash_data_fn)(
71 HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
72 PUCHAR pbInput,
73 ULONG cbInput,
74 ULONG dwFlags);
75
76 typedef NTSTATUS (WINAPI *cng_destroy_hash_fn)(
77 HANDLE /* BCRYPT_HASH_HANDLE */ hHash);
78
79 typedef NTSTATUS (WINAPI *cng_close_algorithm_provider_fn)(
80 HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
81 ULONG dwFlags);
82
83 struct cng_provider {
84 /* DLL for CNG */
85 HINSTANCE dll;
86
87 /* Function pointers for CNG */
88 cng_open_algorithm_provider_fn open_algorithm_provider;
89 cng_get_property_fn get_property;
90 cng_create_hash_fn create_hash;
91 cng_finish_hash_fn finish_hash;
92 cng_hash_data_fn hash_data;
93 cng_destroy_hash_fn destroy_hash;
94 cng_close_algorithm_provider_fn close_algorithm_provider;
95
96 HANDLE /* BCRYPT_ALG_HANDLE */ sha1_handle;
97 DWORD sha1_object_size;
98
99 HANDLE /* BCRYPT_ALG_HANDLE */ sha256_handle;
100 DWORD sha256_object_size;
101 };
102
103 typedef struct {
104 git_hash_win32_provider_t type;
105
106 union {
107 struct cryptoapi_provider cryptoapi;
108 struct cng_provider cng;
109 } provider;
110 } hash_win32_provider;
111
112 /* Hash provider definition */
113
114 static hash_win32_provider hash_provider = {0};
115
116 /* Hash initialization */
117
118 /* Initialize CNG, if available */
119 GIT_INLINE(int) cng_provider_init(void)
120 {
121 char dll_path[MAX_PATH];
122 DWORD dll_path_len, size_len;
123
124 /* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
125 if (!git_has_win32_version(6, 0, 1)) {
126 git_error_set(GIT_ERROR_SHA, "CryptoNG is not supported on this platform");
127 return -1;
128 }
129
130 /* Load bcrypt.dll explicitly from the system directory */
131 if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 ||
132 dll_path_len > MAX_PATH ||
133 StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
134 StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
135 (hash_provider.provider.cng.dll = LoadLibrary(dll_path)) == NULL) {
136 git_error_set(GIT_ERROR_SHA, "CryptoNG library could not be loaded");
137 return -1;
138 }
139
140 /* Load the function addresses */
141 if ((hash_provider.provider.cng.open_algorithm_provider = (cng_open_algorithm_provider_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptOpenAlgorithmProvider"))) == NULL ||
142 (hash_provider.provider.cng.get_property = (cng_get_property_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptGetProperty"))) == NULL ||
143 (hash_provider.provider.cng.create_hash = (cng_create_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptCreateHash"))) == NULL ||
144 (hash_provider.provider.cng.finish_hash = (cng_finish_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptFinishHash"))) == NULL ||
145 (hash_provider.provider.cng.hash_data = (cng_hash_data_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptHashData"))) == NULL ||
146 (hash_provider.provider.cng.destroy_hash = (cng_destroy_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptDestroyHash"))) == NULL ||
147 (hash_provider.provider.cng.close_algorithm_provider = (cng_close_algorithm_provider_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptCloseAlgorithmProvider"))) == NULL) {
148 FreeLibrary(hash_provider.provider.cng.dll);
149
150 git_error_set(GIT_ERROR_OS, "CryptoNG functions could not be loaded");
151 return -1;
152 }
153
154 /* Load the SHA1 algorithm */
155 if (hash_provider.provider.cng.open_algorithm_provider(&hash_provider.provider.cng.sha1_handle, GIT_HASH_CNG_SHA1_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0 ||
156 hash_provider.provider.cng.get_property(hash_provider.provider.cng.sha1_handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_provider.provider.cng.sha1_object_size, sizeof(DWORD), &size_len, 0) < 0) {
157 git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized");
158 goto on_error;
159 }
160
161 /* Load the SHA256 algorithm */
162 if (hash_provider.provider.cng.open_algorithm_provider(&hash_provider.provider.cng.sha256_handle, GIT_HASH_CNG_SHA256_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0 ||
163 hash_provider.provider.cng.get_property(hash_provider.provider.cng.sha256_handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_provider.provider.cng.sha256_object_size, sizeof(DWORD), &size_len, 0) < 0) {
164 git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized");
165 goto on_error;
166 }
167
168 hash_provider.type = GIT_HASH_WIN32_CNG;
169 return 0;
170
171 on_error:
172 if (hash_provider.provider.cng.sha1_handle)
173 hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha1_handle, 0);
174
175 if (hash_provider.provider.cng.sha256_handle)
176 hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha256_handle, 0);
177
178 if (hash_provider.provider.cng.dll)
179 FreeLibrary(hash_provider.provider.cng.dll);
180
181 return -1;
182 }
183
184 GIT_INLINE(void) cng_provider_shutdown(void)
185 {
186 if (hash_provider.type == GIT_HASH_WIN32_INVALID)
187 return;
188
189 hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha1_handle, 0);
190 hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha256_handle, 0);
191 FreeLibrary(hash_provider.provider.cng.dll);
192
193 hash_provider.type = GIT_HASH_WIN32_INVALID;
194 }
195
196 /* Initialize CryptoAPI */
197 GIT_INLINE(int) cryptoapi_provider_init(void)
198 {
199 if (!CryptAcquireContext(&hash_provider.provider.cryptoapi.handle, NULL, 0, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
200 git_error_set(GIT_ERROR_OS, "legacy hash context could not be started");
201 return -1;
202 }
203
204 hash_provider.type = GIT_HASH_WIN32_CRYPTOAPI;
205 return 0;
206 }
207
208 GIT_INLINE(void) cryptoapi_provider_shutdown(void)
209 {
210 if (hash_provider.type == GIT_HASH_WIN32_INVALID)
211 return;
212
213 CryptReleaseContext(hash_provider.provider.cryptoapi.handle, 0);
214
215 hash_provider.type = GIT_HASH_WIN32_INVALID;
216 }
217
218 static void hash_provider_shutdown(void)
219 {
220 if (hash_provider.type == GIT_HASH_WIN32_CNG)
221 cng_provider_shutdown();
222 else if (hash_provider.type == GIT_HASH_WIN32_CRYPTOAPI)
223 cryptoapi_provider_shutdown();
224 }
225
226 static int hash_provider_init(void)
227 {
228 int error = 0;
229
230 if (hash_provider.type != GIT_HASH_WIN32_INVALID)
231 return 0;
232
233 if ((error = cng_provider_init()) < 0)
234 error = cryptoapi_provider_init();
235
236 if (!error)
237 error = git_runtime_shutdown_register(hash_provider_shutdown);
238
239 return error;
240 }
241
242 git_hash_win32_provider_t git_hash_win32_provider(void)
243 {
244 return hash_provider.type;
245 }
246
247 int git_hash_win32_set_provider(git_hash_win32_provider_t provider)
248 {
249 if (provider == hash_provider.type)
250 return 0;
251
252 hash_provider_shutdown();
253
254 if (provider == GIT_HASH_WIN32_CNG)
255 return cng_provider_init();
256 else if (provider == GIT_HASH_WIN32_CRYPTOAPI)
257 return cryptoapi_provider_init();
258
259 git_error_set(GIT_ERROR_SHA, "unsupported win32 provider");
260 return -1;
261 }
262
263 /* CryptoAPI: available in Windows XP and newer */
264
265 GIT_INLINE(int) hash_cryptoapi_init(git_hash_win32_ctx *ctx)
266 {
267 if (ctx->ctx.cryptoapi.valid)
268 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
269
270 if (!CryptCreateHash(hash_provider.provider.cryptoapi.handle, ctx->algorithm, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
271 ctx->ctx.cryptoapi.valid = 0;
272 git_error_set(GIT_ERROR_OS, "legacy hash implementation could not be created");
273 return -1;
274 }
275
276 ctx->ctx.cryptoapi.valid = 1;
277 return 0;
278 }
279
280 GIT_INLINE(int) hash_cryptoapi_update(git_hash_win32_ctx *ctx, const void *_data, size_t len)
281 {
282 const BYTE *data = (BYTE *)_data;
283
284 GIT_ASSERT(ctx->ctx.cryptoapi.valid);
285
286 while (len > 0) {
287 DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len;
288
289 if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) {
290 git_error_set(GIT_ERROR_OS, "legacy hash data could not be updated");
291 return -1;
292 }
293
294 data += chunk;
295 len -= chunk;
296 }
297
298 return 0;
299 }
300
301 GIT_INLINE(int) hash_cryptoapi_final(unsigned char *out, git_hash_win32_ctx *ctx)
302 {
303 DWORD len = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE;
304 int error = 0;
305
306 GIT_ASSERT(ctx->ctx.cryptoapi.valid);
307
308 if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out, &len, 0)) {
309 git_error_set(GIT_ERROR_OS, "legacy hash data could not be finished");
310 error = -1;
311 }
312
313 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
314 ctx->ctx.cryptoapi.valid = 0;
315
316 return error;
317 }
318
319 GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_win32_ctx *ctx)
320 {
321 if (ctx->ctx.cryptoapi.valid)
322 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
323 }
324
325 GIT_INLINE(int) hash_sha1_cryptoapi_ctx_init_init(git_hash_win32_ctx *ctx)
326 {
327 ctx->algorithm = CALG_SHA1;
328 return hash_cryptoapi_init(ctx);
329 }
330
331 GIT_INLINE(int) hash_sha256_cryptoapi_ctx_init(git_hash_win32_ctx *ctx)
332 {
333 ctx->algorithm = CALG_SHA_256;
334 return hash_cryptoapi_init(ctx);
335 }
336
337 /* CNG: Available in Windows Server 2008 and newer */
338
339 GIT_INLINE(int) hash_sha1_cng_ctx_init(git_hash_win32_ctx *ctx)
340 {
341 if ((ctx->ctx.cng.hash_object = git__malloc(hash_provider.provider.cng.sha1_object_size)) == NULL)
342 return -1;
343
344 if (hash_provider.provider.cng.create_hash(hash_provider.provider.cng.sha1_handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_provider.provider.cng.sha1_object_size, NULL, 0, 0) < 0) {
345 git__free(ctx->ctx.cng.hash_object);
346
347 git_error_set(GIT_ERROR_OS, "sha1 implementation could not be created");
348 return -1;
349 }
350
351 ctx->algorithm = CALG_SHA1;
352 return 0;
353 }
354
355 GIT_INLINE(int) hash_sha256_cng_ctx_init(git_hash_win32_ctx *ctx)
356 {
357 if ((ctx->ctx.cng.hash_object = git__malloc(hash_provider.provider.cng.sha256_object_size)) == NULL)
358 return -1;
359
360 if (hash_provider.provider.cng.create_hash(hash_provider.provider.cng.sha256_handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_provider.provider.cng.sha256_object_size, NULL, 0, 0) < 0) {
361 git__free(ctx->ctx.cng.hash_object);
362
363 git_error_set(GIT_ERROR_OS, "sha256 implementation could not be created");
364 return -1;
365 }
366
367 ctx->algorithm = CALG_SHA_256;
368 return 0;
369 }
370
371 GIT_INLINE(int) hash_cng_init(git_hash_win32_ctx *ctx)
372 {
373 BYTE hash[GIT_HASH_SHA256_SIZE];
374 ULONG size = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE;
375
376 if (!ctx->ctx.cng.updated)
377 return 0;
378
379 /* CNG needs to be finished to restart */
380 if (hash_provider.provider.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, size, 0) < 0) {
381 git_error_set(GIT_ERROR_OS, "hash implementation could not be finished");
382 return -1;
383 }
384
385 ctx->ctx.cng.updated = 0;
386
387 return 0;
388 }
389
390 GIT_INLINE(int) hash_cng_update(git_hash_win32_ctx *ctx, const void *_data, size_t len)
391 {
392 PBYTE data = (PBYTE)_data;
393
394 while (len > 0) {
395 ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len;
396
397 if (hash_provider.provider.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) {
398 git_error_set(GIT_ERROR_OS, "hash could not be updated");
399 return -1;
400 }
401
402 data += chunk;
403 len -= chunk;
404 }
405
406 return 0;
407 }
408
409 GIT_INLINE(int) hash_cng_final(unsigned char *out, git_hash_win32_ctx *ctx)
410 {
411 ULONG size = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE;
412
413 if (hash_provider.provider.cng.finish_hash(ctx->ctx.cng.hash_handle, out, size, 0) < 0) {
414 git_error_set(GIT_ERROR_OS, "hash could not be finished");
415 return -1;
416 }
417
418 ctx->ctx.cng.updated = 0;
419
420 return 0;
421 }
422
423 GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_win32_ctx *ctx)
424 {
425 hash_provider.provider.cng.destroy_hash(ctx->ctx.cng.hash_handle);
426 git__free(ctx->ctx.cng.hash_object);
427 }
428
429 /* Indirection between CryptoAPI and CNG */
430
431 GIT_INLINE(int) hash_sha1_win32_ctx_init(git_hash_win32_ctx *ctx)
432 {
433 GIT_ASSERT_ARG(hash_provider.type);
434
435 memset(ctx, 0x0, sizeof(git_hash_win32_ctx));
436 return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_sha1_cng_ctx_init(ctx) : hash_sha1_cryptoapi_ctx_init_init(ctx);
437 }
438
439 GIT_INLINE(int) hash_sha256_win32_ctx_init(git_hash_win32_ctx *ctx)
440 {
441 GIT_ASSERT_ARG(hash_provider.type);
442
443 memset(ctx, 0x0, sizeof(git_hash_win32_ctx));
444 return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_sha256_cng_ctx_init(ctx) : hash_sha256_cryptoapi_ctx_init(ctx);
445 }
446
447 GIT_INLINE(int) hash_win32_init(git_hash_win32_ctx *ctx)
448 {
449 return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx);
450 }
451
452 GIT_INLINE(int) hash_win32_update(git_hash_win32_ctx *ctx, const void *data, size_t len)
453 {
454 return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len);
455 }
456
457 GIT_INLINE(int) hash_win32_final(unsigned char *out, git_hash_win32_ctx *ctx)
458 {
459 GIT_ASSERT_ARG(ctx);
460 return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx);
461 }
462
463 GIT_INLINE(void) hash_win32_cleanup(git_hash_win32_ctx *ctx)
464 {
465 if (hash_provider.type == GIT_HASH_WIN32_CNG)
466 hash_ctx_cng_cleanup(ctx);
467 else if(hash_provider.type == GIT_HASH_WIN32_CRYPTOAPI)
468 hash_ctx_cryptoapi_cleanup(ctx);
469 }
470
471 #ifdef GIT_SHA1_WIN32
472
473 int git_hash_sha1_global_init(void)
474 {
475 return hash_provider_init();
476 }
477
478 int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
479 {
480 GIT_ASSERT_ARG(ctx);
481 return hash_sha1_win32_ctx_init(&ctx->win32);
482 }
483
484 int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
485 {
486 GIT_ASSERT_ARG(ctx);
487 return hash_win32_init(&ctx->win32);
488 }
489
490 int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
491 {
492 GIT_ASSERT_ARG(ctx);
493 return hash_win32_update(&ctx->win32, data, len);
494 }
495
496 int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
497 {
498 GIT_ASSERT_ARG(ctx);
499 return hash_win32_final(out, &ctx->win32);
500 }
501
502 void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
503 {
504 if (!ctx)
505 return;
506 hash_win32_cleanup(&ctx->win32);
507 }
508
509 #endif
510
511 #ifdef GIT_SHA256_WIN32
512
513 int git_hash_sha256_global_init(void)
514 {
515 return hash_provider_init();
516 }
517
518 int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
519 {
520 GIT_ASSERT_ARG(ctx);
521 return hash_sha256_win32_ctx_init(&ctx->win32);
522 }
523
524 int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
525 {
526 GIT_ASSERT_ARG(ctx);
527 return hash_win32_init(&ctx->win32);
528 }
529
530 int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len)
531 {
532 GIT_ASSERT_ARG(ctx);
533 return hash_win32_update(&ctx->win32, data, len);
534 }
535
536 int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
537 {
538 GIT_ASSERT_ARG(ctx);
539 return hash_win32_final(out, &ctx->win32);
540 }
541
542 void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
543 {
544 if (!ctx)
545 return;
546 hash_win32_cleanup(&ctx->win32);
547 }
548
549 #endif