2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
15 #define GIT_HASH_CNG_DLL_NAME "bcrypt.dll"
17 /* BCRYPT_SHA1_ALGORITHM */
18 #define GIT_HASH_CNG_SHA1_TYPE L"SHA1"
19 #define GIT_HASH_CNG_SHA256_TYPE L"SHA256"
21 /* BCRYPT_OBJECT_LENGTH */
22 #define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength"
24 /* BCRYPT_HASH_REUSEABLE_FLAGS */
25 #define GIT_HASH_CNG_HASH_REUSABLE 0x00000020
29 /* CryptoAPI is available for hashing on Windows XP and newer. */
30 struct cryptoapi_provider
{
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.
41 /* Function declarations for CNG */
42 typedef NTSTATUS (WINAPI
*cng_open_algorithm_provider_fn
)(
43 HANDLE
/* BCRYPT_ALG_HANDLE */ *phAlgorithm
,
45 LPCWSTR pszImplementation
,
48 typedef NTSTATUS (WINAPI
*cng_get_property_fn
)(
49 HANDLE
/* BCRYPT_HANDLE */ hObject
,
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
,
64 typedef NTSTATUS (WINAPI
*cng_finish_hash_fn
)(
65 HANDLE
/* BCRYPT_HASH_HANDLE */ hHash
,
70 typedef NTSTATUS (WINAPI
*cng_hash_data_fn
)(
71 HANDLE
/* BCRYPT_HASH_HANDLE */ hHash
,
76 typedef NTSTATUS (WINAPI
*cng_destroy_hash_fn
)(
77 HANDLE
/* BCRYPT_HASH_HANDLE */ hHash
);
79 typedef NTSTATUS (WINAPI
*cng_close_algorithm_provider_fn
)(
80 HANDLE
/* BCRYPT_ALG_HANDLE */ hAlgorithm
,
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
;
96 HANDLE
/* BCRYPT_ALG_HANDLE */ sha1_handle
;
97 DWORD sha1_object_size
;
99 HANDLE
/* BCRYPT_ALG_HANDLE */ sha256_handle
;
100 DWORD sha256_object_size
;
104 git_hash_win32_provider_t type
;
107 struct cryptoapi_provider cryptoapi
;
108 struct cng_provider cng
;
110 } hash_win32_provider
;
112 /* Hash provider definition */
114 static hash_win32_provider hash_provider
= {0};
116 /* Hash initialization */
118 /* Initialize CNG, if available */
119 GIT_INLINE(int) cng_provider_init(void)
121 char dll_path
[MAX_PATH
];
122 DWORD dll_path_len
, size_len
;
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");
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");
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
);
150 git_error_set(GIT_ERROR_OS
, "CryptoNG functions could not be loaded");
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");
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");
168 hash_provider
.type
= GIT_HASH_WIN32_CNG
;
172 if (hash_provider
.provider
.cng
.sha1_handle
)
173 hash_provider
.provider
.cng
.close_algorithm_provider(hash_provider
.provider
.cng
.sha1_handle
, 0);
175 if (hash_provider
.provider
.cng
.sha256_handle
)
176 hash_provider
.provider
.cng
.close_algorithm_provider(hash_provider
.provider
.cng
.sha256_handle
, 0);
178 if (hash_provider
.provider
.cng
.dll
)
179 FreeLibrary(hash_provider
.provider
.cng
.dll
);
184 GIT_INLINE(void) cng_provider_shutdown(void)
186 if (hash_provider
.type
== GIT_HASH_WIN32_INVALID
)
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
);
193 hash_provider
.type
= GIT_HASH_WIN32_INVALID
;
196 /* Initialize CryptoAPI */
197 GIT_INLINE(int) cryptoapi_provider_init(void)
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");
204 hash_provider
.type
= GIT_HASH_WIN32_CRYPTOAPI
;
208 GIT_INLINE(void) cryptoapi_provider_shutdown(void)
210 if (hash_provider
.type
== GIT_HASH_WIN32_INVALID
)
213 CryptReleaseContext(hash_provider
.provider
.cryptoapi
.handle
, 0);
215 hash_provider
.type
= GIT_HASH_WIN32_INVALID
;
218 static void hash_provider_shutdown(void)
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();
226 static int hash_provider_init(void)
230 if (hash_provider
.type
!= GIT_HASH_WIN32_INVALID
)
233 if ((error
= cng_provider_init()) < 0)
234 error
= cryptoapi_provider_init();
237 error
= git_runtime_shutdown_register(hash_provider_shutdown
);
242 git_hash_win32_provider_t
git_hash_win32_provider(void)
244 return hash_provider
.type
;
247 int git_hash_win32_set_provider(git_hash_win32_provider_t provider
)
249 if (provider
== hash_provider
.type
)
252 hash_provider_shutdown();
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();
259 git_error_set(GIT_ERROR_SHA
, "unsupported win32 provider");
263 /* CryptoAPI: available in Windows XP and newer */
265 GIT_INLINE(int) hash_cryptoapi_init(git_hash_win32_ctx
*ctx
)
267 if (ctx
->ctx
.cryptoapi
.valid
)
268 CryptDestroyHash(ctx
->ctx
.cryptoapi
.hash_handle
);
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");
276 ctx
->ctx
.cryptoapi
.valid
= 1;
280 GIT_INLINE(int) hash_cryptoapi_update(git_hash_win32_ctx
*ctx
, const void *_data
, size_t len
)
282 const BYTE
*data
= (BYTE
*)_data
;
284 GIT_ASSERT(ctx
->ctx
.cryptoapi
.valid
);
287 DWORD chunk
= (len
> MAXDWORD
) ? MAXDWORD
: (DWORD
)len
;
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");
301 GIT_INLINE(int) hash_cryptoapi_final(unsigned char *out
, git_hash_win32_ctx
*ctx
)
303 DWORD len
= ctx
->algorithm
== CALG_SHA_256
? GIT_HASH_SHA256_SIZE
: GIT_HASH_SHA1_SIZE
;
306 GIT_ASSERT(ctx
->ctx
.cryptoapi
.valid
);
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");
313 CryptDestroyHash(ctx
->ctx
.cryptoapi
.hash_handle
);
314 ctx
->ctx
.cryptoapi
.valid
= 0;
319 GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_win32_ctx
*ctx
)
321 if (ctx
->ctx
.cryptoapi
.valid
)
322 CryptDestroyHash(ctx
->ctx
.cryptoapi
.hash_handle
);
325 GIT_INLINE(int) hash_sha1_cryptoapi_ctx_init_init(git_hash_win32_ctx
*ctx
)
327 ctx
->algorithm
= CALG_SHA1
;
328 return hash_cryptoapi_init(ctx
);
331 GIT_INLINE(int) hash_sha256_cryptoapi_ctx_init(git_hash_win32_ctx
*ctx
)
333 ctx
->algorithm
= CALG_SHA_256
;
334 return hash_cryptoapi_init(ctx
);
337 /* CNG: Available in Windows Server 2008 and newer */
339 GIT_INLINE(int) hash_sha1_cng_ctx_init(git_hash_win32_ctx
*ctx
)
341 if ((ctx
->ctx
.cng
.hash_object
= git__malloc(hash_provider
.provider
.cng
.sha1_object_size
)) == NULL
)
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
);
347 git_error_set(GIT_ERROR_OS
, "sha1 implementation could not be created");
351 ctx
->algorithm
= CALG_SHA1
;
355 GIT_INLINE(int) hash_sha256_cng_ctx_init(git_hash_win32_ctx
*ctx
)
357 if ((ctx
->ctx
.cng
.hash_object
= git__malloc(hash_provider
.provider
.cng
.sha256_object_size
)) == NULL
)
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
);
363 git_error_set(GIT_ERROR_OS
, "sha256 implementation could not be created");
367 ctx
->algorithm
= CALG_SHA_256
;
371 GIT_INLINE(int) hash_cng_init(git_hash_win32_ctx
*ctx
)
373 BYTE hash
[GIT_HASH_SHA256_SIZE
];
374 ULONG size
= ctx
->algorithm
== CALG_SHA_256
? GIT_HASH_SHA256_SIZE
: GIT_HASH_SHA1_SIZE
;
376 if (!ctx
->ctx
.cng
.updated
)
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");
385 ctx
->ctx
.cng
.updated
= 0;
390 GIT_INLINE(int) hash_cng_update(git_hash_win32_ctx
*ctx
, const void *_data
, size_t len
)
392 PBYTE data
= (PBYTE
)_data
;
395 ULONG chunk
= (len
> ULONG_MAX
) ? ULONG_MAX
: (ULONG
)len
;
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");
409 GIT_INLINE(int) hash_cng_final(unsigned char *out
, git_hash_win32_ctx
*ctx
)
411 ULONG size
= ctx
->algorithm
== CALG_SHA_256
? GIT_HASH_SHA256_SIZE
: GIT_HASH_SHA1_SIZE
;
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");
418 ctx
->ctx
.cng
.updated
= 0;
423 GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_win32_ctx
*ctx
)
425 hash_provider
.provider
.cng
.destroy_hash(ctx
->ctx
.cng
.hash_handle
);
426 git__free(ctx
->ctx
.cng
.hash_object
);
429 /* Indirection between CryptoAPI and CNG */
431 GIT_INLINE(int) hash_sha1_win32_ctx_init(git_hash_win32_ctx
*ctx
)
433 GIT_ASSERT_ARG(hash_provider
.type
);
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
);
439 GIT_INLINE(int) hash_sha256_win32_ctx_init(git_hash_win32_ctx
*ctx
)
441 GIT_ASSERT_ARG(hash_provider
.type
);
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
);
447 GIT_INLINE(int) hash_win32_init(git_hash_win32_ctx
*ctx
)
449 return (hash_provider
.type
== GIT_HASH_WIN32_CNG
) ? hash_cng_init(ctx
) : hash_cryptoapi_init(ctx
);
452 GIT_INLINE(int) hash_win32_update(git_hash_win32_ctx
*ctx
, const void *data
, size_t len
)
454 return (hash_provider
.type
== GIT_HASH_WIN32_CNG
) ? hash_cng_update(ctx
, data
, len
) : hash_cryptoapi_update(ctx
, data
, len
);
457 GIT_INLINE(int) hash_win32_final(unsigned char *out
, git_hash_win32_ctx
*ctx
)
460 return (hash_provider
.type
== GIT_HASH_WIN32_CNG
) ? hash_cng_final(out
, ctx
) : hash_cryptoapi_final(out
, ctx
);
463 GIT_INLINE(void) hash_win32_cleanup(git_hash_win32_ctx
*ctx
)
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
);
471 #ifdef GIT_SHA1_WIN32
473 int git_hash_sha1_global_init(void)
475 return hash_provider_init();
478 int git_hash_sha1_ctx_init(git_hash_sha1_ctx
*ctx
)
481 return hash_sha1_win32_ctx_init(&ctx
->win32
);
484 int git_hash_sha1_init(git_hash_sha1_ctx
*ctx
)
487 return hash_win32_init(&ctx
->win32
);
490 int git_hash_sha1_update(git_hash_sha1_ctx
*ctx
, const void *data
, size_t len
)
493 return hash_win32_update(&ctx
->win32
, data
, len
);
496 int git_hash_sha1_final(unsigned char *out
, git_hash_sha1_ctx
*ctx
)
499 return hash_win32_final(out
, &ctx
->win32
);
502 void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx
*ctx
)
506 hash_win32_cleanup(&ctx
->win32
);
511 #ifdef GIT_SHA256_WIN32
513 int git_hash_sha256_global_init(void)
515 return hash_provider_init();
518 int git_hash_sha256_ctx_init(git_hash_sha256_ctx
*ctx
)
521 return hash_sha256_win32_ctx_init(&ctx
->win32
);
524 int git_hash_sha256_init(git_hash_sha256_ctx
*ctx
)
527 return hash_win32_init(&ctx
->win32
);
530 int git_hash_sha256_update(git_hash_sha256_ctx
*ctx
, const void *data
, size_t len
)
533 return hash_win32_update(&ctx
->win32
, data
, len
);
536 int git_hash_sha256_final(unsigned char *out
, git_hash_sha256_ctx
*ctx
)
539 return hash_win32_final(out
, &ctx
->win32
);
542 void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx
*ctx
)
546 hash_win32_cleanup(&ctx
->win32
);