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_HASH_TYPE L"SHA1"
20 /* BCRYPT_OBJECT_LENGTH */
21 #define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength"
23 /* BCRYPT_HASH_REUSEABLE_FLAGS */
24 #define GIT_HASH_CNG_HASH_REUSABLE 0x00000020
26 static git_hash_prov hash_prov
= {0};
28 /* Hash initialization */
30 /* Initialize CNG, if available */
31 GIT_INLINE(int) hash_cng_prov_init(void)
33 char dll_path
[MAX_PATH
];
34 DWORD dll_path_len
, size_len
;
36 /* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
37 if (!git_has_win32_version(6, 0, 1)) {
38 git_error_set(GIT_ERROR_SHA1
, "CryptoNG is not supported on this platform");
42 /* Load bcrypt.dll explicitly from the system directory */
43 if ((dll_path_len
= GetSystemDirectory(dll_path
, MAX_PATH
)) == 0 ||
44 dll_path_len
> MAX_PATH
||
45 StringCchCat(dll_path
, MAX_PATH
, "\\") < 0 ||
46 StringCchCat(dll_path
, MAX_PATH
, GIT_HASH_CNG_DLL_NAME
) < 0 ||
47 (hash_prov
.prov
.cng
.dll
= LoadLibrary(dll_path
)) == NULL
) {
48 git_error_set(GIT_ERROR_SHA1
, "CryptoNG library could not be loaded");
52 /* Load the function addresses */
53 if ((hash_prov
.prov
.cng
.open_algorithm_provider
= (hash_win32_cng_open_algorithm_provider_fn
)GetProcAddress(hash_prov
.prov
.cng
.dll
, "BCryptOpenAlgorithmProvider")) == NULL
||
54 (hash_prov
.prov
.cng
.get_property
= (hash_win32_cng_get_property_fn
)GetProcAddress(hash_prov
.prov
.cng
.dll
, "BCryptGetProperty")) == NULL
||
55 (hash_prov
.prov
.cng
.create_hash
= (hash_win32_cng_create_hash_fn
)GetProcAddress(hash_prov
.prov
.cng
.dll
, "BCryptCreateHash")) == NULL
||
56 (hash_prov
.prov
.cng
.finish_hash
= (hash_win32_cng_finish_hash_fn
)GetProcAddress(hash_prov
.prov
.cng
.dll
, "BCryptFinishHash")) == NULL
||
57 (hash_prov
.prov
.cng
.hash_data
= (hash_win32_cng_hash_data_fn
)GetProcAddress(hash_prov
.prov
.cng
.dll
, "BCryptHashData")) == NULL
||
58 (hash_prov
.prov
.cng
.destroy_hash
= (hash_win32_cng_destroy_hash_fn
)GetProcAddress(hash_prov
.prov
.cng
.dll
, "BCryptDestroyHash")) == NULL
||
59 (hash_prov
.prov
.cng
.close_algorithm_provider
= (hash_win32_cng_close_algorithm_provider_fn
)GetProcAddress(hash_prov
.prov
.cng
.dll
, "BCryptCloseAlgorithmProvider")) == NULL
) {
60 FreeLibrary(hash_prov
.prov
.cng
.dll
);
62 git_error_set(GIT_ERROR_OS
, "CryptoNG functions could not be loaded");
66 /* Load the SHA1 algorithm */
67 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) {
68 FreeLibrary(hash_prov
.prov
.cng
.dll
);
70 git_error_set(GIT_ERROR_OS
, "algorithm provider could not be initialized");
74 /* Get storage space for the hash object */
75 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) {
76 hash_prov
.prov
.cng
.close_algorithm_provider(hash_prov
.prov
.cng
.handle
, 0);
77 FreeLibrary(hash_prov
.prov
.cng
.dll
);
79 git_error_set(GIT_ERROR_OS
, "algorithm handle could not be found");
87 GIT_INLINE(void) hash_cng_prov_shutdown(void)
89 hash_prov
.prov
.cng
.close_algorithm_provider(hash_prov
.prov
.cng
.handle
, 0);
90 FreeLibrary(hash_prov
.prov
.cng
.dll
);
92 hash_prov
.type
= INVALID
;
95 /* Initialize CryptoAPI */
96 GIT_INLINE(int) hash_cryptoapi_prov_init()
98 if (!CryptAcquireContext(&hash_prov
.prov
.cryptoapi
.handle
, NULL
, 0, PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
)) {
99 git_error_set(GIT_ERROR_OS
, "legacy hash context could not be started");
103 hash_prov
.type
= CRYPTOAPI
;
107 GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
109 CryptReleaseContext(hash_prov
.prov
.cryptoapi
.handle
, 0);
111 hash_prov
.type
= INVALID
;
114 static void sha1_shutdown(void)
116 if (hash_prov
.type
== CNG
)
117 hash_cng_prov_shutdown();
118 else if(hash_prov
.type
== CRYPTOAPI
)
119 hash_cryptoapi_prov_shutdown();
122 int git_hash_sha1_global_init(void)
126 if (hash_prov
.type
!= INVALID
)
129 if ((error
= hash_cng_prov_init()) < 0)
130 error
= hash_cryptoapi_prov_init();
132 git__on_shutdown(sha1_shutdown
);
137 /* CryptoAPI: available in Windows XP and newer */
139 GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_sha1_ctx
*ctx
)
141 ctx
->type
= CRYPTOAPI
;
142 ctx
->prov
= &hash_prov
;
144 return git_hash_sha1_init(ctx
);
147 GIT_INLINE(int) hash_cryptoapi_init(git_hash_sha1_ctx
*ctx
)
149 if (ctx
->ctx
.cryptoapi
.valid
)
150 CryptDestroyHash(ctx
->ctx
.cryptoapi
.hash_handle
);
152 if (!CryptCreateHash(ctx
->prov
->prov
.cryptoapi
.handle
, CALG_SHA1
, 0, 0, &ctx
->ctx
.cryptoapi
.hash_handle
)) {
153 ctx
->ctx
.cryptoapi
.valid
= 0;
154 git_error_set(GIT_ERROR_OS
, "legacy hash implementation could not be created");
158 ctx
->ctx
.cryptoapi
.valid
= 1;
162 GIT_INLINE(int) hash_cryptoapi_update(git_hash_sha1_ctx
*ctx
, const void *_data
, size_t len
)
164 const BYTE
*data
= (BYTE
*)_data
;
166 assert(ctx
->ctx
.cryptoapi
.valid
);
169 DWORD chunk
= (len
> MAXDWORD
) ? MAXDWORD
: (DWORD
)len
;
171 if (!CryptHashData(ctx
->ctx
.cryptoapi
.hash_handle
, data
, chunk
, 0)) {
172 git_error_set(GIT_ERROR_OS
, "legacy hash data could not be updated");
183 GIT_INLINE(int) hash_cryptoapi_final(git_oid
*out
, git_hash_sha1_ctx
*ctx
)
188 assert(ctx
->ctx
.cryptoapi
.valid
);
190 if (!CryptGetHashParam(ctx
->ctx
.cryptoapi
.hash_handle
, HP_HASHVAL
, out
->id
, &len
, 0)) {
191 git_error_set(GIT_ERROR_OS
, "legacy hash data could not be finished");
195 CryptDestroyHash(ctx
->ctx
.cryptoapi
.hash_handle
);
196 ctx
->ctx
.cryptoapi
.valid
= 0;
201 GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_sha1_ctx
*ctx
)
203 if (ctx
->ctx
.cryptoapi
.valid
)
204 CryptDestroyHash(ctx
->ctx
.cryptoapi
.hash_handle
);
207 /* CNG: Available in Windows Server 2008 and newer */
209 GIT_INLINE(int) hash_ctx_cng_init(git_hash_sha1_ctx
*ctx
)
211 if ((ctx
->ctx
.cng
.hash_object
= git__malloc(hash_prov
.prov
.cng
.hash_object_size
)) == NULL
)
214 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) {
215 git__free(ctx
->ctx
.cng
.hash_object
);
217 git_error_set(GIT_ERROR_OS
, "hash implementation could not be created");
222 ctx
->prov
= &hash_prov
;
227 GIT_INLINE(int) hash_cng_init(git_hash_sha1_ctx
*ctx
)
229 BYTE hash
[GIT_OID_RAWSZ
];
231 if (!ctx
->ctx
.cng
.updated
)
234 /* CNG needs to be finished to restart */
235 if (ctx
->prov
->prov
.cng
.finish_hash(ctx
->ctx
.cng
.hash_handle
, hash
, GIT_OID_RAWSZ
, 0) < 0) {
236 git_error_set(GIT_ERROR_OS
, "hash implementation could not be finished");
240 ctx
->ctx
.cng
.updated
= 0;
245 GIT_INLINE(int) hash_cng_update(git_hash_sha1_ctx
*ctx
, const void *_data
, size_t len
)
247 PBYTE data
= (PBYTE
)_data
;
250 ULONG chunk
= (len
> ULONG_MAX
) ? ULONG_MAX
: (ULONG
)len
;
252 if (ctx
->prov
->prov
.cng
.hash_data(ctx
->ctx
.cng
.hash_handle
, data
, chunk
, 0) < 0) {
253 git_error_set(GIT_ERROR_OS
, "hash could not be updated");
264 GIT_INLINE(int) hash_cng_final(git_oid
*out
, git_hash_sha1_ctx
*ctx
)
266 if (ctx
->prov
->prov
.cng
.finish_hash(ctx
->ctx
.cng
.hash_handle
, out
->id
, GIT_OID_RAWSZ
, 0) < 0) {
267 git_error_set(GIT_ERROR_OS
, "hash could not be finished");
271 ctx
->ctx
.cng
.updated
= 0;
276 GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_sha1_ctx
*ctx
)
278 ctx
->prov
->prov
.cng
.destroy_hash(ctx
->ctx
.cng
.hash_handle
);
279 git__free(ctx
->ctx
.cng
.hash_object
);
282 /* Indirection between CryptoAPI and CNG */
284 int git_hash_sha1_ctx_init(git_hash_sha1_ctx
*ctx
)
291 * When compiled with GIT_THREADS, the global hash_prov data is
292 * initialized with git_libgit2_init. Otherwise, it must be initialized
295 if (hash_prov
.type
== INVALID
&& (error
= git_hash_sha1_global_init()) < 0)
298 memset(ctx
, 0x0, sizeof(git_hash_sha1_ctx
));
300 return (hash_prov
.type
== CNG
) ? hash_ctx_cng_init(ctx
) : hash_ctx_cryptoapi_init(ctx
);
303 int git_hash_sha1_init(git_hash_sha1_ctx
*ctx
)
305 assert(ctx
&& ctx
->type
);
306 return (ctx
->type
== CNG
) ? hash_cng_init(ctx
) : hash_cryptoapi_init(ctx
);
309 int git_hash_sha1_update(git_hash_sha1_ctx
*ctx
, const void *data
, size_t len
)
311 assert(ctx
&& ctx
->type
);
312 return (ctx
->type
== CNG
) ? hash_cng_update(ctx
, data
, len
) : hash_cryptoapi_update(ctx
, data
, len
);
315 int git_hash_sha1_final(git_oid
*out
, git_hash_sha1_ctx
*ctx
)
317 assert(ctx
&& ctx
->type
);
318 return (ctx
->type
== CNG
) ? hash_cng_final(out
, ctx
) : hash_cryptoapi_final(out
, ctx
);
321 void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx
*ctx
)
325 if (ctx
->type
== CNG
)
326 hash_ctx_cng_cleanup(ctx
);
327 else if(ctx
->type
== CRYPTOAPI
)
328 hash_ctx_cryptoapi_cleanup(ctx
);