Skip to content

Commit d35db3e

Browse files
committed
Add checksum implementation based on BCrypt (which is a build-in cryptography
provider on Windows). Unlike OpenSSL, it's not comparable to the speed of light but it still earns its place in the supersonic fleet. Please refer to results of my measurements bellow: [[[ APR (win32) 0.11 GB/s (baseline) APR (Linux) 0.16 GB/s BCrypt 0.54 GB/s (added in this revision) OpenSSL (Linux) 1.38 GB/s OpenSSL (win32) 1.51 GB/s ]]] * CMakeLists.txt (SVN_CHECKSUM_BACKEND): Mention new option. (bcrypt): Implement setup for this module. * subversion/include/svn_error_codes.h (SVN_ERR_BCRYPT): New error code. * subversion/libsvn_subr/checksum_bcrypt.c: Implement new module. (handle_error): New function. (algorithm_state_t): New structure. (md5, sha1): Declare algorithm states. (algorithm_init): New function (sorry for the use of some bird language). (bcrypt_ctx_t, bcrypt_ctx_cleanup, bcrypt_ctx_init, bcrypt_ctx_update, bcrypt_ctx_final, bcrypt_checksum): New structure+methods to work with it. (svn_checksum__md5, svn_checksum__md5_ctx_t, svn_checksum__md5_ctx_create, svn_checksum__md5_ctx_reset, svn_checksum__md5_ctx_update, svn_checksum__md5_ctx_final, svn_checksum__sha1, svn_checksum__sha1_ctx_t, svn_checksum__sha1_ctx_create, svn_checksum__sha1_ctx_reset, svn_checksum__sha1_ctx_update, svn_checksum__sha1_ctx_final): Implement the module API. ### Note: Future me has to fix that part where checksum backend is being set up by the 'openssl' target in build system, because it doesn't make that much sense now. git-svn-id: https://svn.apache.org/repos/asf/subversion/trunk@1931019 13f79535-47bb-0310-9956-ffa450edef68
1 parent 3483399 commit d35db3e

File tree

3 files changed

+306
-2
lines changed

3 files changed

+306
-2
lines changed

CMakeLists.txt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ cmake_dependent_option(SVN_BUILD_SHARED_RA "Build shared RA modules" ON "BUILD_S
111111
option(SVN_DEBUG "Enables specific features for developer builds" OFF)
112112
cmake_dependent_option(SVN_USE_WIN32_CRASHHANDLER "Enables WIN32 crash handler." ON "WIN32" OFF)
113113
option(SVN_USE_DSO "Defined if svn should try to load DSOs" OFF)
114-
set(SVN_CHECKSUM_BACKEND "apr" CACHE STRING "Checksum backend to use (possible values are 'apr', 'openssl')" )
115-
set_property(CACHE SVN_CHECKSUM_BACKEND PROPERTY STRINGS "apr" "openssl")
114+
set(SVN_CHECKSUM_BACKEND "apr" CACHE STRING "Checksum backend to use (possible values are 'apr', 'openssl', or 'bcrypt')" )
115+
set_property(CACHE SVN_CHECKSUM_BACKEND PROPERTY STRINGS "apr" "openssl" "bcrypt")
116116
set(SVN_SOVERSION "0" CACHE STRING "Subversion library ABI version")
117117
mark_as_advanced(SVN_SOVERSION)
118118

@@ -398,6 +398,18 @@ elseif (SVN_CHECKSUM_BACKEND STREQUAL "openssl")
398398
"Defined if svn should use OpenSSL to compute checksums."
399399
"SVN_CHECKSUM_BACKEND_OPENSSL" "1"
400400
)
401+
elseif (SVN_CHECKSUM_BACKEND STREQUAL "bcrypt")
402+
if (NOT WIN32)
403+
message(SEND_ERROR "BCrypt checksum backend is only available on Win32 platform.")
404+
endif()
405+
406+
add_library(external-openssl INTERFACE)
407+
target_link_libraries(external-openssl INTERFACE Bcrypt.lib)
408+
409+
add_private_config_definition(
410+
"Defined if svn should use BCrypt to compute checksums."
411+
"SVN_CHECKSUM_BACKEND_BCRYPT" "1"
412+
)
401413
else()
402414
message(SEND_ERROR
403415
"Invalid value of SVN_CHECKSUM_BACKEND: '${SVN_CHECKSUM_BACKEND}'. "

subversion/include/svn_error_codes.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,11 @@ SVN_ERROR_START
15181518
SVN_ERR_MISC_CATEGORY_START + 47,
15191519
"Could not canonicalize path or URI")
15201520

1521+
/** @since New in 1.16. */
1522+
SVN_ERRDEF(SVN_ERR_BCRYPT,
1523+
SVN_ERR_MISC_CATEGORY_START + 48,
1524+
"BCrypt method failed.")
1525+
15211526
/* command-line client errors */
15221527

15231528
SVN_ERRDEF(SVN_ERR_CL_ARG_PARSING_ERROR,
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
/*
2+
* checksum_bcrypt.c: BCrypt backed checksums
3+
*
4+
* ====================================================================
5+
* Licensed to the Apache Software Foundation (ASF) under one
6+
* or more contributor license agreements. See the NOTICE file
7+
* distributed with this work for additional information
8+
* regarding copyright ownership. The ASF licenses this file
9+
* to you under the Apache License, Version 2.0 (the
10+
* "License"); you may not use this file except in compliance
11+
* with the License. You may obtain a copy of the License at
12+
*
13+
* http://www.apache.org/licenses/LICENSE-2.0
14+
*
15+
* Unless required by applicable law or agreed to in writing,
16+
* software distributed under the License is distributed on an
17+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18+
* KIND, either express or implied. See the License for the
19+
* specific language governing permissions and limitations
20+
* under the License.
21+
* ====================================================================
22+
*/
23+
24+
#include "svn_private_config.h"
25+
#ifdef SVN_CHECKSUM_BACKEND_BCRYPT
26+
27+
#include <windows.h>
28+
#include <bcrypt.h>
29+
30+
#include "svn_error.h"
31+
#include "private/svn_atomic.h"
32+
#include "checksum.h"
33+
34+
static svn_error_t *
35+
handle_error(NTSTATUS status)
36+
{
37+
if (BCRYPT_SUCCESS(status))
38+
return SVN_NO_ERROR;
39+
else
40+
return svn_error_create(SVN_ERR_BCRYPT, NULL, NULL);
41+
}
42+
43+
44+
/* State of the algorithm as we load it. */
45+
typedef struct algorithm_state_t
46+
{
47+
LPCWSTR alg_name;
48+
BCRYPT_ALG_HANDLE alg_handle;
49+
50+
svn_atomic_t initialized;
51+
52+
DWORD hash_length;
53+
DWORD object_length;
54+
} algorithm_state_t;
55+
56+
static algorithm_state_t md5 = { BCRYPT_MD5_ALGORITHM, 0, 0, 0, 0 };
57+
static algorithm_state_t sha1 = { BCRYPT_SHA1_ALGORITHM, 0, 0, 0, 0 };
58+
59+
/* This implements svn_atomic__err_init_func_t */
60+
static svn_error_t *
61+
algorithm_init(void *baton, apr_pool_t *null_pool)
62+
{
63+
algorithm_state_t *state = (algorithm_state_t *)baton;
64+
ULONG cb_result;
65+
66+
SVN_ERR(handle_error(BCryptOpenAlgorithmProvider(&state->alg_handle,
67+
state->alg_name,
68+
MS_PRIMITIVE_PROVIDER,
69+
/* dwFlags */ 0)));
70+
71+
SVN_ERR(handle_error(BCryptGetProperty(state->alg_handle,
72+
BCRYPT_HASH_LENGTH,
73+
(PUCHAR) &state->hash_length,
74+
sizeof(state->hash_length),
75+
&cb_result,
76+
/* dwFlags */ 0)));
77+
78+
SVN_ERR(handle_error(BCryptGetProperty(state->alg_handle,
79+
BCRYPT_OBJECT_LENGTH,
80+
(PUCHAR) &state->object_length,
81+
sizeof(state->object_length),
82+
&cb_result,
83+
/* dwFlags */ 0)));
84+
return SVN_NO_ERROR;
85+
}
86+
87+
88+
/* An abstract wrapper over BCrypt checksum API. */
89+
typedef struct bcrypt_ctx_t
90+
{
91+
BCRYPT_HASH_HANDLE handle;
92+
const algorithm_state_t *algorithm;
93+
} bcrypt_ctx_t;
94+
95+
/* A cleanup handler. */
96+
static apr_status_t
97+
bcrypt_ctx_cleanup(void *data)
98+
{
99+
bcrypt_ctx_t *ctx = (bcrypt_ctx_t *)data;
100+
101+
if (! BCRYPT_SUCCESS(BCryptDestroyHash(ctx->handle)))
102+
SVN_ERR_MALFUNCTION_NO_RETURN();
103+
104+
return APR_SUCCESS;
105+
}
106+
107+
static svn_error_t *
108+
bcrypt_ctx_init(bcrypt_ctx_t *ctx,
109+
algorithm_state_t *algorithm)
110+
{
111+
BCRYPT_HASH_HANDLE handle;
112+
113+
SVN_ERR(svn_atomic__init_once(&algorithm->initialized, algorithm_init,
114+
algorithm, NULL));
115+
116+
SVN_ERR(handle_error(BCryptCreateHash(algorithm->alg_handle,
117+
&handle,
118+
NULL, 0,
119+
/* pbSecret */ NULL,
120+
/* cbSecret */ 0,
121+
/* dwFlags */ 0)));
122+
123+
ctx->handle = handle;
124+
ctx->algorithm = algorithm;
125+
return SVN_NO_ERROR;
126+
}
127+
128+
static svn_error_t *
129+
bcrypt_ctx_update(bcrypt_ctx_t *ctx,
130+
const void *data,
131+
apr_size_t len)
132+
{
133+
SVN_ERR_ASSERT(len <= ULONG_MAX);
134+
135+
SVN_ERR(handle_error(BCryptHashData(ctx->handle,
136+
(PUCHAR) data,
137+
(ULONG) len,
138+
/* dwFlags */ 0)));
139+
140+
return SVN_NO_ERROR;
141+
}
142+
143+
static svn_error_t *
144+
bcrypt_ctx_final(algorithm_state_t *algorithm,
145+
unsigned char *digest,
146+
bcrypt_ctx_t *ctx)
147+
{
148+
SVN_ERR(handle_error(BCryptFinishHash(ctx->handle,
149+
(PUCHAR) digest,
150+
algorithm->hash_length,
151+
/* dwFlags */ 0)));
152+
153+
return SVN_NO_ERROR;
154+
}
155+
156+
static svn_error_t *
157+
bcrypt_checksum(algorithm_state_t *algorithm,
158+
unsigned char *digest,
159+
const void *data,
160+
apr_size_t len)
161+
{
162+
bcrypt_ctx_t bcrypt_ctx;
163+
svn_error_t *err = SVN_NO_ERROR;
164+
165+
SVN_ERR(bcrypt_ctx_init(&bcrypt_ctx, algorithm));
166+
167+
err = bcrypt_ctx_update(&bcrypt_ctx, data, len);
168+
if (err)
169+
{
170+
bcrypt_ctx_cleanup(&bcrypt_ctx);
171+
return err;
172+
}
173+
174+
SVN_ERR(bcrypt_ctx_final(algorithm, digest, &bcrypt_ctx));
175+
176+
bcrypt_ctx_cleanup(&bcrypt_ctx);
177+
return err;
178+
}
179+
180+
181+
/*** MD5 checksum ***/
182+
svn_error_t *
183+
svn_checksum__md5(unsigned char *digest,
184+
const void *data,
185+
apr_size_t len)
186+
{
187+
return svn_error_trace(bcrypt_checksum(&md5, digest, data, len));
188+
}
189+
190+
struct svn_checksum__md5_ctx_t
191+
{
192+
bcrypt_ctx_t bcrypt_ctx;
193+
};
194+
195+
svn_checksum__md5_ctx_t *
196+
svn_checksum__md5_ctx_create(apr_pool_t *pool)
197+
{
198+
svn_checksum__md5_ctx_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
199+
svn_error_t *err;
200+
201+
err = bcrypt_ctx_init(&ctx->bcrypt_ctx, &md5);
202+
SVN_ERR_ASSERT_NO_RETURN(err == SVN_NO_ERROR);
203+
204+
apr_pool_cleanup_register(pool, &ctx->bcrypt_ctx, bcrypt_ctx_cleanup, NULL);
205+
206+
return ctx;
207+
}
208+
209+
svn_error_t *
210+
svn_checksum__md5_ctx_reset(svn_checksum__md5_ctx_t *ctx)
211+
{
212+
bcrypt_ctx_cleanup(&ctx->bcrypt_ctx);
213+
SVN_ERR(bcrypt_ctx_init(&ctx->bcrypt_ctx, &md5));
214+
215+
return SVN_NO_ERROR;
216+
}
217+
218+
svn_error_t *
219+
svn_checksum__md5_ctx_update(svn_checksum__md5_ctx_t *ctx,
220+
const void *data,
221+
apr_size_t len)
222+
{
223+
return svn_error_trace(bcrypt_ctx_update(&ctx->bcrypt_ctx, data,
224+
len));
225+
}
226+
227+
svn_error_t *
228+
svn_checksum__md5_ctx_final(unsigned char *digest,
229+
svn_checksum__md5_ctx_t *ctx)
230+
{
231+
return svn_error_trace(bcrypt_ctx_final(&md5, digest, &ctx->bcrypt_ctx));
232+
}
233+
234+
235+
/*** SHA1 checksum ***/
236+
svn_error_t *
237+
svn_checksum__sha1(unsigned char *digest,
238+
const void *data,
239+
apr_size_t len)
240+
{
241+
return svn_error_trace(bcrypt_checksum(&sha1, digest, data, len));
242+
}
243+
244+
struct svn_checksum__sha1_ctx_t
245+
{
246+
bcrypt_ctx_t bcrypt_ctx;
247+
};
248+
249+
svn_checksum__sha1_ctx_t *
250+
svn_checksum__sha1_ctx_create(apr_pool_t *pool)
251+
{
252+
svn_checksum__sha1_ctx_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
253+
svn_error_t *err;
254+
255+
err = bcrypt_ctx_init(&ctx->bcrypt_ctx, &sha1);
256+
SVN_ERR_ASSERT_NO_RETURN(err == SVN_NO_ERROR);
257+
258+
apr_pool_cleanup_register(pool, &ctx->bcrypt_ctx, bcrypt_ctx_cleanup, NULL);
259+
260+
return ctx;
261+
}
262+
263+
svn_error_t *
264+
svn_checksum__sha1_ctx_reset(svn_checksum__sha1_ctx_t *ctx)
265+
{
266+
bcrypt_ctx_cleanup(&ctx->bcrypt_ctx);
267+
SVN_ERR(bcrypt_ctx_init(&ctx->bcrypt_ctx, &sha1));
268+
269+
return SVN_NO_ERROR;
270+
}
271+
272+
svn_error_t *
273+
svn_checksum__sha1_ctx_update(svn_checksum__sha1_ctx_t *ctx,
274+
const void *data,
275+
apr_size_t len)
276+
{
277+
return svn_error_trace(bcrypt_ctx_update(&ctx->bcrypt_ctx, data, len));
278+
}
279+
280+
svn_error_t *
281+
svn_checksum__sha1_ctx_final(unsigned char *digest,
282+
svn_checksum__sha1_ctx_t *ctx)
283+
{
284+
return svn_error_trace(bcrypt_ctx_final(&sha1, digest, &ctx->bcrypt_ctx));
285+
}
286+
287+
#endif /* SVN_CHECKSUM_BACKEND_APR */

0 commit comments

Comments
 (0)