]> git.proxmox.com Git - libgit2.git/blame_incremental - src/hash/sha1/win32.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / hash / sha1 / win32.c
... / ...
CommitLineData
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_HASH_TYPE L"SHA1"
19
20/* BCRYPT_OBJECT_LENGTH */
21#define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength"
22
23/* BCRYPT_HASH_REUSEABLE_FLAGS */
24#define GIT_HASH_CNG_HASH_REUSABLE 0x00000020
25
26static git_hash_prov hash_prov = {0};
27
28/* Hash initialization */
29
30/* Initialize CNG, if available */
31GIT_INLINE(int) hash_cng_prov_init(void)
32{
33 char dll_path[MAX_PATH];
34 DWORD dll_path_len, size_len;
35
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");
39 return -1;
40 }
41
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");
49 return -1;
50 }
51
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);
61
62 git_error_set(GIT_ERROR_OS, "CryptoNG functions could not be loaded");
63 return -1;
64 }
65
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);
69
70 git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized");
71 return -1;
72 }
73
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);
78
79 git_error_set(GIT_ERROR_OS, "algorithm handle could not be found");
80 return -1;
81 }
82
83 hash_prov.type = CNG;
84 return 0;
85}
86
87GIT_INLINE(void) hash_cng_prov_shutdown(void)
88{
89 hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
90 FreeLibrary(hash_prov.prov.cng.dll);
91
92 hash_prov.type = INVALID;
93}
94
95/* Initialize CryptoAPI */
96GIT_INLINE(int) hash_cryptoapi_prov_init()
97{
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");
100 return -1;
101 }
102
103 hash_prov.type = CRYPTOAPI;
104 return 0;
105}
106
107GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
108{
109 CryptReleaseContext(hash_prov.prov.cryptoapi.handle, 0);
110
111 hash_prov.type = INVALID;
112}
113
114static void sha1_shutdown(void)
115{
116 if (hash_prov.type == CNG)
117 hash_cng_prov_shutdown();
118 else if(hash_prov.type == CRYPTOAPI)
119 hash_cryptoapi_prov_shutdown();
120}
121
122int git_hash_sha1_global_init(void)
123{
124 int error = 0;
125
126 if (hash_prov.type != INVALID)
127 return 0;
128
129 if ((error = hash_cng_prov_init()) < 0)
130 error = hash_cryptoapi_prov_init();
131
132 if (!error)
133 error = git_runtime_shutdown_register(sha1_shutdown);
134
135 return error;
136}
137
138/* CryptoAPI: available in Windows XP and newer */
139
140GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_sha1_ctx *ctx)
141{
142 ctx->type = CRYPTOAPI;
143 ctx->prov = &hash_prov;
144
145 return git_hash_sha1_init(ctx);
146}
147
148GIT_INLINE(int) hash_cryptoapi_init(git_hash_sha1_ctx *ctx)
149{
150 if (ctx->ctx.cryptoapi.valid)
151 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
152
153 if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
154 ctx->ctx.cryptoapi.valid = 0;
155 git_error_set(GIT_ERROR_OS, "legacy hash implementation could not be created");
156 return -1;
157 }
158
159 ctx->ctx.cryptoapi.valid = 1;
160 return 0;
161}
162
163GIT_INLINE(int) hash_cryptoapi_update(git_hash_sha1_ctx *ctx, const void *_data, size_t len)
164{
165 const BYTE *data = (BYTE *)_data;
166
167 GIT_ASSERT(ctx->ctx.cryptoapi.valid);
168
169 while (len > 0) {
170 DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len;
171
172 if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) {
173 git_error_set(GIT_ERROR_OS, "legacy hash data could not be updated");
174 return -1;
175 }
176
177 data += chunk;
178 len -= chunk;
179 }
180
181 return 0;
182}
183
184GIT_INLINE(int) hash_cryptoapi_final(unsigned char *out, git_hash_sha1_ctx *ctx)
185{
186 DWORD len = GIT_HASH_SHA1_SIZE;
187 int error = 0;
188
189 GIT_ASSERT(ctx->ctx.cryptoapi.valid);
190
191 if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out, &len, 0)) {
192 git_error_set(GIT_ERROR_OS, "legacy hash data could not be finished");
193 error = -1;
194 }
195
196 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
197 ctx->ctx.cryptoapi.valid = 0;
198
199 return error;
200}
201
202GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_sha1_ctx *ctx)
203{
204 if (ctx->ctx.cryptoapi.valid)
205 CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
206}
207
208/* CNG: Available in Windows Server 2008 and newer */
209
210GIT_INLINE(int) hash_ctx_cng_init(git_hash_sha1_ctx *ctx)
211{
212 if ((ctx->ctx.cng.hash_object = git__malloc(hash_prov.prov.cng.hash_object_size)) == NULL)
213 return -1;
214
215 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) {
216 git__free(ctx->ctx.cng.hash_object);
217
218 git_error_set(GIT_ERROR_OS, "hash implementation could not be created");
219 return -1;
220 }
221
222 ctx->type = CNG;
223 ctx->prov = &hash_prov;
224
225 return 0;
226}
227
228GIT_INLINE(int) hash_cng_init(git_hash_sha1_ctx *ctx)
229{
230 BYTE hash[GIT_OID_RAWSZ];
231
232 if (!ctx->ctx.cng.updated)
233 return 0;
234
235 /* CNG needs to be finished to restart */
236 if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0) {
237 git_error_set(GIT_ERROR_OS, "hash implementation could not be finished");
238 return -1;
239 }
240
241 ctx->ctx.cng.updated = 0;
242
243 return 0;
244}
245
246GIT_INLINE(int) hash_cng_update(git_hash_sha1_ctx *ctx, const void *_data, size_t len)
247{
248 PBYTE data = (PBYTE)_data;
249
250 while (len > 0) {
251 ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len;
252
253 if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) {
254 git_error_set(GIT_ERROR_OS, "hash could not be updated");
255 return -1;
256 }
257
258 data += chunk;
259 len -= chunk;
260 }
261
262 return 0;
263}
264
265GIT_INLINE(int) hash_cng_final(unsigned char *out, git_hash_sha1_ctx *ctx)
266{
267 if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out, GIT_HASH_SHA1_SIZE, 0) < 0) {
268 git_error_set(GIT_ERROR_OS, "hash could not be finished");
269 return -1;
270 }
271
272 ctx->ctx.cng.updated = 0;
273
274 return 0;
275}
276
277GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_sha1_ctx *ctx)
278{
279 ctx->prov->prov.cng.destroy_hash(ctx->ctx.cng.hash_handle);
280 git__free(ctx->ctx.cng.hash_object);
281}
282
283/* Indirection between CryptoAPI and CNG */
284
285int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
286{
287 int error = 0;
288
289 GIT_ASSERT_ARG(ctx);
290
291 /*
292 * When compiled with GIT_THREADS, the global hash_prov data is
293 * initialized with git_libgit2_init. Otherwise, it must be initialized
294 * at first use.
295 */
296 if (hash_prov.type == INVALID && (error = git_hash_sha1_global_init()) < 0)
297 return error;
298
299 memset(ctx, 0x0, sizeof(git_hash_sha1_ctx));
300
301 return (hash_prov.type == CNG) ? hash_ctx_cng_init(ctx) : hash_ctx_cryptoapi_init(ctx);
302}
303
304int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
305{
306 GIT_ASSERT_ARG(ctx);
307 GIT_ASSERT_ARG(ctx->type);
308 return (ctx->type == CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx);
309}
310
311int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
312{
313 GIT_ASSERT_ARG(ctx);
314 GIT_ASSERT_ARG(ctx->type);
315 return (ctx->type == CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len);
316}
317
318int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
319{
320 GIT_ASSERT_ARG(ctx);
321 GIT_ASSERT_ARG(ctx->type);
322 return (ctx->type == CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx);
323}
324
325void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
326{
327 if (!ctx)
328 return;
329 else if (ctx->type == CNG)
330 hash_ctx_cng_cleanup(ctx);
331 else if(ctx->type == CRYPTOAPI)
332 hash_ctx_cryptoapi_cleanup(ctx);
333}