Author: rinrab
Date: Wed Dec 31 21:43:19 2025
New Revision: 1931019
Log:
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.
Added:
subversion/trunk/subversion/libsvn_subr/checksum_bcrypt.c (contents, props
changed)
Modified:
subversion/trunk/CMakeLists.txt
subversion/trunk/subversion/include/svn_error_codes.h
Modified: subversion/trunk/CMakeLists.txt
==============================================================================
--- subversion/trunk/CMakeLists.txt Wed Dec 31 19:26:37 2025
(r1931018)
+++ subversion/trunk/CMakeLists.txt Wed Dec 31 21:43:19 2025
(r1931019)
@@ -111,8 +111,8 @@ cmake_dependent_option(SVN_BUILD_SHARED_
option(SVN_DEBUG "Enables specific features for developer builds" OFF)
cmake_dependent_option(SVN_USE_WIN32_CRASHHANDLER "Enables WIN32 crash
handler." ON "WIN32" OFF)
option(SVN_USE_DSO "Defined if svn should try to load DSOs" OFF)
-set(SVN_CHECKSUM_BACKEND "apr" CACHE STRING "Checksum backend to use (possible
values are 'apr', 'openssl')" )
-set_property(CACHE SVN_CHECKSUM_BACKEND PROPERTY STRINGS "apr" "openssl")
+set(SVN_CHECKSUM_BACKEND "apr" CACHE STRING "Checksum backend to use (possible
values are 'apr', 'openssl', or 'bcrypt')" )
+set_property(CACHE SVN_CHECKSUM_BACKEND PROPERTY STRINGS "apr" "openssl"
"bcrypt")
set(SVN_SOVERSION "0" CACHE STRING "Subversion library ABI version")
mark_as_advanced(SVN_SOVERSION)
@@ -398,6 +398,18 @@ elseif (SVN_CHECKSUM_BACKEND STREQUAL "o
"Defined if svn should use OpenSSL to compute checksums."
"SVN_CHECKSUM_BACKEND_OPENSSL" "1"
)
+elseif (SVN_CHECKSUM_BACKEND STREQUAL "bcrypt")
+ if (NOT WIN32)
+ message(SEND_ERROR "BCrypt checksum backend is only available on Win32
platform.")
+ endif()
+
+ add_library(external-openssl INTERFACE)
+ target_link_libraries(external-openssl INTERFACE Bcrypt.lib)
+
+ add_private_config_definition(
+ "Defined if svn should use BCrypt to compute checksums."
+ "SVN_CHECKSUM_BACKEND_BCRYPT" "1"
+ )
else()
message(SEND_ERROR
"Invalid value of SVN_CHECKSUM_BACKEND: '${SVN_CHECKSUM_BACKEND}'. "
Modified: subversion/trunk/subversion/include/svn_error_codes.h
==============================================================================
--- subversion/trunk/subversion/include/svn_error_codes.h Wed Dec 31
19:26:37 2025 (r1931018)
+++ subversion/trunk/subversion/include/svn_error_codes.h Wed Dec 31
21:43:19 2025 (r1931019)
@@ -1518,6 +1518,11 @@ SVN_ERROR_START
SVN_ERR_MISC_CATEGORY_START + 47,
"Could not canonicalize path or URI")
+ /** @since New in 1.16. */
+ SVN_ERRDEF(SVN_ERR_BCRYPT,
+ SVN_ERR_MISC_CATEGORY_START + 48,
+ "BCrypt method failed.")
+
/* command-line client errors */
SVN_ERRDEF(SVN_ERR_CL_ARG_PARSING_ERROR,
Added: subversion/trunk/subversion/libsvn_subr/checksum_bcrypt.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ subversion/trunk/subversion/libsvn_subr/checksum_bcrypt.c Wed Dec 31
21:43:19 2025 (r1931019)
@@ -0,0 +1,287 @@
+/*
+ * checksum_bcrypt.c: BCrypt backed checksums
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include "svn_private_config.h"
+#ifdef SVN_CHECKSUM_BACKEND_BCRYPT
+
+#include <windows.h>
+#include <bcrypt.h>
+
+#include "svn_error.h"
+#include "private/svn_atomic.h"
+#include "checksum.h"
+
+static svn_error_t *
+handle_error(NTSTATUS status)
+{
+ if (BCRYPT_SUCCESS(status))
+ return SVN_NO_ERROR;
+ else
+ return svn_error_create(SVN_ERR_BCRYPT, NULL, NULL);
+}
+
+
+/* State of the algorithm as we load it. */
+typedef struct algorithm_state_t
+{
+ LPCWSTR alg_name;
+ BCRYPT_ALG_HANDLE alg_handle;
+
+ svn_atomic_t initialized;
+
+ DWORD hash_length;
+ DWORD object_length;
+} algorithm_state_t;
+
+static algorithm_state_t md5 = { BCRYPT_MD5_ALGORITHM, 0, 0, 0, 0 };
+static algorithm_state_t sha1 = { BCRYPT_SHA1_ALGORITHM, 0, 0, 0, 0 };
+
+/* This implements svn_atomic__err_init_func_t */
+static svn_error_t *
+algorithm_init(void *baton, apr_pool_t *null_pool)
+{
+ algorithm_state_t *state = (algorithm_state_t *)baton;
+ ULONG cb_result;
+
+ SVN_ERR(handle_error(BCryptOpenAlgorithmProvider(&state->alg_handle,
+ state->alg_name,
+ MS_PRIMITIVE_PROVIDER,
+ /* dwFlags */ 0)));
+
+ SVN_ERR(handle_error(BCryptGetProperty(state->alg_handle,
+ BCRYPT_HASH_LENGTH,
+ (PUCHAR) &state->hash_length,
+ sizeof(state->hash_length),
+ &cb_result,
+ /* dwFlags */ 0)));
+
+ SVN_ERR(handle_error(BCryptGetProperty(state->alg_handle,
+ BCRYPT_OBJECT_LENGTH,
+ (PUCHAR) &state->object_length,
+ sizeof(state->object_length),
+ &cb_result,
+ /* dwFlags */ 0)));
+ return SVN_NO_ERROR;
+}
+
+
+/* An abstract wrapper over BCrypt checksum API. */
+typedef struct bcrypt_ctx_t
+{
+ BCRYPT_HASH_HANDLE handle;
+ const algorithm_state_t *algorithm;
+} bcrypt_ctx_t;
+
+/* A cleanup handler. */
+static apr_status_t
+bcrypt_ctx_cleanup(void *data)
+{
+ bcrypt_ctx_t *ctx = (bcrypt_ctx_t *)data;
+
+ if (! BCRYPT_SUCCESS(BCryptDestroyHash(ctx->handle)))
+ SVN_ERR_MALFUNCTION_NO_RETURN();
+
+ return APR_SUCCESS;
+}
+
+static svn_error_t *
+bcrypt_ctx_init(bcrypt_ctx_t *ctx,
+ algorithm_state_t *algorithm)
+{
+ BCRYPT_HASH_HANDLE handle;
+
+ SVN_ERR(svn_atomic__init_once(&algorithm->initialized, algorithm_init,
+ algorithm, NULL));
+
+ SVN_ERR(handle_error(BCryptCreateHash(algorithm->alg_handle,
+ &handle,
+ NULL, 0,
+ /* pbSecret */ NULL,
+ /* cbSecret */ 0,
+ /* dwFlags */ 0)));
+
+ ctx->handle = handle;
+ ctx->algorithm = algorithm;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+bcrypt_ctx_update(bcrypt_ctx_t *ctx,
+ const void *data,
+ apr_size_t len)
+{
+ SVN_ERR_ASSERT(len <= ULONG_MAX);
+
+ SVN_ERR(handle_error(BCryptHashData(ctx->handle,
+ (PUCHAR) data,
+ (ULONG) len,
+ /* dwFlags */ 0)));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+bcrypt_ctx_final(algorithm_state_t *algorithm,
+ unsigned char *digest,
+ bcrypt_ctx_t *ctx)
+{
+ SVN_ERR(handle_error(BCryptFinishHash(ctx->handle,
+ (PUCHAR) digest,
+ algorithm->hash_length,
+ /* dwFlags */ 0)));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+bcrypt_checksum(algorithm_state_t *algorithm,
+ unsigned char *digest,
+ const void *data,
+ apr_size_t len)
+{
+ bcrypt_ctx_t bcrypt_ctx;
+ svn_error_t *err = SVN_NO_ERROR;
+
+ SVN_ERR(bcrypt_ctx_init(&bcrypt_ctx, algorithm));
+
+ err = bcrypt_ctx_update(&bcrypt_ctx, data, len);
+ if (err)
+ {
+ bcrypt_ctx_cleanup(&bcrypt_ctx);
+ return err;
+ }
+
+ SVN_ERR(bcrypt_ctx_final(algorithm, digest, &bcrypt_ctx));
+
+ bcrypt_ctx_cleanup(&bcrypt_ctx);
+ return err;
+}
+
+
+/*** MD5 checksum ***/
+svn_error_t *
+svn_checksum__md5(unsigned char *digest,
+ const void *data,
+ apr_size_t len)
+{
+ return svn_error_trace(bcrypt_checksum(&md5, digest, data, len));
+}
+
+struct svn_checksum__md5_ctx_t
+{
+ bcrypt_ctx_t bcrypt_ctx;
+};
+
+svn_checksum__md5_ctx_t *
+svn_checksum__md5_ctx_create(apr_pool_t *pool)
+{
+ svn_checksum__md5_ctx_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
+ svn_error_t *err;
+
+ err = bcrypt_ctx_init(&ctx->bcrypt_ctx, &md5);
+ SVN_ERR_ASSERT_NO_RETURN(err == SVN_NO_ERROR);
+
+ apr_pool_cleanup_register(pool, &ctx->bcrypt_ctx, bcrypt_ctx_cleanup, NULL);
+
+ return ctx;
+}
+
+svn_error_t *
+svn_checksum__md5_ctx_reset(svn_checksum__md5_ctx_t *ctx)
+{
+ bcrypt_ctx_cleanup(&ctx->bcrypt_ctx);
+ SVN_ERR(bcrypt_ctx_init(&ctx->bcrypt_ctx, &md5));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_checksum__md5_ctx_update(svn_checksum__md5_ctx_t *ctx,
+ const void *data,
+ apr_size_t len)
+{
+ return svn_error_trace(bcrypt_ctx_update(&ctx->bcrypt_ctx, data,
+ len));
+}
+
+svn_error_t *
+svn_checksum__md5_ctx_final(unsigned char *digest,
+ svn_checksum__md5_ctx_t *ctx)
+{
+ return svn_error_trace(bcrypt_ctx_final(&md5, digest, &ctx->bcrypt_ctx));
+}
+
+
+/*** SHA1 checksum ***/
+svn_error_t *
+svn_checksum__sha1(unsigned char *digest,
+ const void *data,
+ apr_size_t len)
+{
+ return svn_error_trace(bcrypt_checksum(&sha1, digest, data, len));
+}
+
+struct svn_checksum__sha1_ctx_t
+{
+ bcrypt_ctx_t bcrypt_ctx;
+};
+
+svn_checksum__sha1_ctx_t *
+svn_checksum__sha1_ctx_create(apr_pool_t *pool)
+{
+ svn_checksum__sha1_ctx_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
+ svn_error_t *err;
+
+ err = bcrypt_ctx_init(&ctx->bcrypt_ctx, &sha1);
+ SVN_ERR_ASSERT_NO_RETURN(err == SVN_NO_ERROR);
+
+ apr_pool_cleanup_register(pool, &ctx->bcrypt_ctx, bcrypt_ctx_cleanup, NULL);
+
+ return ctx;
+}
+
+svn_error_t *
+svn_checksum__sha1_ctx_reset(svn_checksum__sha1_ctx_t *ctx)
+{
+ bcrypt_ctx_cleanup(&ctx->bcrypt_ctx);
+ SVN_ERR(bcrypt_ctx_init(&ctx->bcrypt_ctx, &sha1));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_checksum__sha1_ctx_update(svn_checksum__sha1_ctx_t *ctx,
+ const void *data,
+ apr_size_t len)
+{
+ return svn_error_trace(bcrypt_ctx_update(&ctx->bcrypt_ctx, data, len));
+}
+
+svn_error_t *
+svn_checksum__sha1_ctx_final(unsigned char *digest,
+ svn_checksum__sha1_ctx_t *ctx)
+{
+ return svn_error_trace(bcrypt_ctx_final(&sha1, digest, &ctx->bcrypt_ctx));
+}
+
+#endif /* SVN_CHECKSUM_BACKEND_APR */