diff options
Diffstat (limited to 'subversion/libsvn_subr/crypto.c')
-rw-r--r-- | subversion/libsvn_subr/crypto.c | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/crypto.c b/subversion/libsvn_subr/crypto.c new file mode 100644 index 000000000000..f3611a1e050a --- /dev/null +++ b/subversion/libsvn_subr/crypto.c @@ -0,0 +1,705 @@ +/* + * crypto.c : cryptographic routines + * + * ==================================================================== + * 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 "crypto.h" + +#ifdef SVN_HAVE_CRYPTO +#include <apr_random.h> +#include <apr_crypto.h> +#endif /* SVN_HAVE_CRYPTO */ + +#include "svn_types.h" +#include "svn_checksum.h" + +#include "svn_private_config.h" +#include "private/svn_atomic.h" + + +/* 1000 iterations is the recommended minimum, per RFC 2898, section 4.2. */ +#define NUM_ITERATIONS 1000 + + +/* Size (in bytes) of the random data we'll prepend to encrypted data. */ +#define RANDOM_PREFIX_LEN 4 + + +/* A structure for containing Subversion's cryptography-related bits + (so we can avoid passing around APR-isms outside this module). */ +struct svn_crypto__ctx_t { +#ifdef SVN_HAVE_CRYPTO + apr_crypto_t *crypto; /* APR cryptography context. */ + +#if 0 + /* ### For now, we will use apr_generate_random_bytes(). If we need + ### more strength, then we can set this member using + ### apr_random_standard_new(), then use + ### apr_generate_random_bytes() to generate entropy for seeding + ### apr_random_t. See httpd/server/core.c:ap_init_rng() */ + apr_random_t *rand; +#endif /* 0 */ +#else /* SVN_HAVE_CRYPTO */ + int unused_but_required_to_satisfy_c_compilers; +#endif /* SVN_HAVE_CRYPTO */ +}; + + + +/*** Helper Functions ***/ +#ifdef SVN_HAVE_CRYPTO + + +/* One-time initialization of the cryptography subsystem. */ +static volatile svn_atomic_t crypto_init_state = 0; + + +#define CRYPTO_INIT(scratch_pool) \ + SVN_ERR(svn_atomic__init_once(&crypto_init_state, \ + crypto_init, NULL, (scratch_pool))) + + +/* Initialize the APR cryptography subsystem (if available), using + ANY_POOL's ancestor root pool for the registration of cleanups, + shutdowns, etc. */ +/* Don't call this function directly! Use svn_atomic__init_once(). */ +static svn_error_t * +crypto_init(void *baton, apr_pool_t *any_pool) +{ + /* NOTE: this function will locate the topmost ancestor of ANY_POOL + for its cleanup handlers. We don't have to worry about ANY_POOL + being cleared. */ + apr_status_t apr_err = apr_crypto_init(any_pool); + if (apr_err) + return svn_error_wrap_apr(apr_err, + _("Failed to initialize cryptography " + "subsystem")); + + return SVN_NO_ERROR; +} + + +/* If APU_ERR is non-NULL, create and return a Subversion error using + APR_ERR and APU_ERR. */ +static svn_error_t * +err_from_apu_err(apr_status_t apr_err, + const apu_err_t *apu_err) +{ + if (apu_err) + return svn_error_createf(apr_err, NULL, + _("code (%d), reason (\"%s\"), msg (\"%s\")"), + apu_err->rc, + apu_err->reason ? apu_err->reason : "", + apu_err->msg ? apu_err->msg : ""); + return SVN_NO_ERROR; +} + + +/* Generate a Subversion error which describes the state reflected by + APR_ERR and any crypto errors registered with CTX. */ +static svn_error_t * +crypto_error_create(svn_crypto__ctx_t *ctx, + apr_status_t apr_err, + const char *msg) +{ + const apu_err_t *apu_err; + apr_status_t rv = apr_crypto_error(&apu_err, ctx->crypto); + svn_error_t *child; + + /* Ugh. The APIs are a bit slippery, so be wary. */ + if (apr_err == APR_SUCCESS) + apr_err = APR_EGENERAL; + + if (rv == APR_SUCCESS) + child = err_from_apu_err(apr_err, apu_err); + else + child = svn_error_wrap_apr(rv, _("Fetching error from APR")); + + return svn_error_create(apr_err, child, msg); +} + + +/* Set RAND_BYTES to a block of bytes containing random data RAND_LEN + long and allocated from RESULT_POOL. */ +static svn_error_t * +get_random_bytes(const unsigned char **rand_bytes, + svn_crypto__ctx_t *ctx, + apr_size_t rand_len, + apr_pool_t *result_pool) +{ + apr_status_t apr_err; + unsigned char *bytes; + + bytes = apr_palloc(result_pool, rand_len); + apr_err = apr_generate_random_bytes(bytes, rand_len); + if (apr_err != APR_SUCCESS) + return svn_error_wrap_apr(apr_err, _("Error obtaining random data")); + + *rand_bytes = bytes; + return SVN_NO_ERROR; +} + + +/* Return an svn_string_t allocated from RESULT_POOL, with its .data + and .len members set to DATA and LEN, respective. + + WARNING: No lifetime management of DATA is offered here, so you + probably want to ensure that that information is allocated in a + sufficiently long-lived pool (such as, for example, RESULT_POOL). */ +static const svn_string_t * +wrap_as_string(const unsigned char *data, + apr_size_t len, + apr_pool_t *result_pool) +{ + svn_string_t *s = apr_palloc(result_pool, sizeof(*s)); + + s->data = (const char *)data; /* better already be in RESULT_POOL */ + s->len = len; + return s; +} + + +#endif /* SVN_HAVE_CRYPTO */ + + + +/*** Semi-public APIs ***/ + +/* Return TRUE iff Subversion's cryptographic support is available. */ +svn_boolean_t svn_crypto__is_available(void) +{ +#ifdef SVN_HAVE_CRYPTO + return TRUE; +#else /* SVN_HAVE_CRYPTO */ + return FALSE; +#endif /* SVN_HAVE_CRYPTO */ +} + + +/* Set CTX to a Subversion cryptography context allocated from + RESULT_POOL. */ +svn_error_t * +svn_crypto__context_create(svn_crypto__ctx_t **ctx, + apr_pool_t *result_pool) +{ +#ifdef SVN_HAVE_CRYPTO + apr_status_t apr_err; + const apu_err_t *apu_err = NULL; + apr_crypto_t *apr_crypto; + const apr_crypto_driver_t *driver; + + CRYPTO_INIT(result_pool); + + /* Load the crypto driver. + + ### TODO: For the sake of flexibility, should we use + ### APU_CRYPTO_RECOMMENDED_DRIVER instead of hard coding + ### "openssl" here? + + NOTE: Potential bugs in get_driver() imply we might get + APR_SUCCESS and NULL. Sigh. Just be a little more careful in + error generation here. */ + apr_err = apr_crypto_get_driver(&driver, "openssl", NULL, &apu_err, + result_pool); + if (apr_err != APR_SUCCESS) + return svn_error_create(apr_err, err_from_apu_err(apr_err, apu_err), + _("OpenSSL crypto driver error")); + if (driver == NULL) + return svn_error_create(APR_EGENERAL, + err_from_apu_err(APR_EGENERAL, apu_err), + _("Bad return value while loading crypto " + "driver")); + + apr_err = apr_crypto_make(&apr_crypto, driver, NULL, result_pool); + if (apr_err != APR_SUCCESS || apr_crypto == NULL) + return svn_error_create(apr_err, NULL, + _("Error creating OpenSSL crypto context")); + + /* Allocate and initialize our crypto context. */ + *ctx = apr_palloc(result_pool, sizeof(**ctx)); + (*ctx)->crypto = apr_crypto; + + return SVN_NO_ERROR; +#else /* SVN_HAVE_CRYPTO */ + return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + "Cryptographic support is not available"); +#endif /* SVN_HAVE_CRYPTO */ +} + + +svn_error_t * +svn_crypto__encrypt_password(const svn_string_t **ciphertext, + const svn_string_t **iv, + const svn_string_t **salt, + svn_crypto__ctx_t *ctx, + const char *password, + const svn_string_t *master, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ +#ifdef SVN_HAVE_CRYPTO + svn_error_t *err = SVN_NO_ERROR; + const unsigned char *salt_vector; + const unsigned char *iv_vector; + apr_size_t iv_len; + apr_crypto_key_t *key = NULL; + apr_status_t apr_err; + const unsigned char *prefix; + apr_crypto_block_t *block_ctx = NULL; + apr_size_t block_size; + unsigned char *assembled; + apr_size_t password_len, assembled_len = 0; + apr_size_t result_len; + unsigned char *result; + apr_size_t ignored_result_len = 0; + + SVN_ERR_ASSERT(ctx != NULL); + + /* Generate the salt. */ +#define SALT_LEN 8 + SVN_ERR(get_random_bytes(&salt_vector, ctx, SALT_LEN, result_pool)); + + /* Initialize the passphrase. */ + apr_err = apr_crypto_passphrase(&key, &iv_len, + master->data, master->len, + salt_vector, SALT_LEN, + APR_KEY_AES_256, APR_MODE_CBC, + FALSE /* doPad */, NUM_ITERATIONS, + ctx->crypto, + scratch_pool); + if (apr_err != APR_SUCCESS) + return svn_error_trace(crypto_error_create( + ctx, apr_err, + _("Error creating derived key"))); + if (! key) + return svn_error_create(APR_EGENERAL, NULL, + _("Error creating derived key")); + if (iv_len == 0) + return svn_error_create(APR_EGENERAL, NULL, + _("Unexpected IV length returned")); + + /* Generate the proper length IV. */ + SVN_ERR(get_random_bytes(&iv_vector, ctx, iv_len, result_pool)); + + /* Initialize block encryption. */ + apr_err = apr_crypto_block_encrypt_init(&block_ctx, &iv_vector, key, + &block_size, scratch_pool); + if ((apr_err != APR_SUCCESS) || (! block_ctx)) + return svn_error_trace(crypto_error_create( + ctx, apr_err, + _("Error initializing block encryption"))); + + /* Generate a 4-byte prefix. */ + SVN_ERR(get_random_bytes(&prefix, ctx, RANDOM_PREFIX_LEN, scratch_pool)); + + /* Combine our prefix, original password, and appropriate padding. + We won't bother padding if the prefix and password combined + perfectly align on the block boundary. If they don't, + however, we'll drop a NUL byte after the password and pad with + random stuff after that to the block boundary. */ + password_len = strlen(password); + assembled_len = RANDOM_PREFIX_LEN + password_len; + if ((assembled_len % block_size) == 0) + { + assembled = apr_palloc(scratch_pool, assembled_len); + memcpy(assembled, prefix, RANDOM_PREFIX_LEN); + memcpy(assembled + RANDOM_PREFIX_LEN, password, password_len); + } + else + { + const unsigned char *padding; + apr_size_t pad_len = block_size - (assembled_len % block_size) - 1; + + SVN_ERR(get_random_bytes(&padding, ctx, pad_len, scratch_pool)); + assembled_len = assembled_len + 1 + pad_len; + assembled = apr_palloc(scratch_pool, assembled_len); + memcpy(assembled, prefix, RANDOM_PREFIX_LEN); + memcpy(assembled + RANDOM_PREFIX_LEN, password, password_len); + *(assembled + RANDOM_PREFIX_LEN + password_len) = '\0'; + memcpy(assembled + RANDOM_PREFIX_LEN + password_len + 1, + padding, pad_len); + } + + /* Get the length that we need to allocate. */ + apr_err = apr_crypto_block_encrypt(NULL, &result_len, assembled, + assembled_len, block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error fetching result length")); + goto cleanup; + } + + /* Allocate our result buffer. */ + result = apr_palloc(result_pool, result_len); + + /* Encrypt the block. */ + apr_err = apr_crypto_block_encrypt(&result, &result_len, assembled, + assembled_len, block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error during block encryption")); + goto cleanup; + } + + /* Finalize the block encryption. Since we padded everything, this should + not produce any more encrypted output. */ + apr_err = apr_crypto_block_encrypt_finish(NULL, + &ignored_result_len, + block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error finalizing block encryption")); + goto cleanup; + } + + *ciphertext = wrap_as_string(result, result_len, result_pool); + *iv = wrap_as_string(iv_vector, iv_len, result_pool); + *salt = wrap_as_string(salt_vector, SALT_LEN, result_pool); + + cleanup: + apr_crypto_block_cleanup(block_ctx); + return err; +#else /* SVN_HAVE_CRYPTO */ + return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + "Cryptographic support is not available"); +#endif /* SVN_HAVE_CRYPTO */ +} + + +svn_error_t * +svn_crypto__decrypt_password(const char **plaintext, + svn_crypto__ctx_t *ctx, + const svn_string_t *ciphertext, + const svn_string_t *iv, + const svn_string_t *salt, + const svn_string_t *master, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ +#ifdef SVN_HAVE_CRYPTO + svn_error_t *err = SVN_NO_ERROR; + apr_status_t apr_err; + apr_crypto_block_t *block_ctx = NULL; + apr_size_t block_size, iv_len; + apr_crypto_key_t *key = NULL; + unsigned char *result; + apr_size_t result_len = 0, final_len = 0; + + /* Initialize the passphrase. */ + apr_err = apr_crypto_passphrase(&key, &iv_len, + master->data, master->len, + (unsigned char *)salt->data, salt->len, + APR_KEY_AES_256, APR_MODE_CBC, + FALSE /* doPad */, NUM_ITERATIONS, + ctx->crypto, scratch_pool); + if (apr_err != APR_SUCCESS) + return svn_error_trace(crypto_error_create( + ctx, apr_err, + _("Error creating derived key"))); + if (! key) + return svn_error_create(APR_EGENERAL, NULL, + _("Error creating derived key")); + if (iv_len == 0) + return svn_error_create(APR_EGENERAL, NULL, + _("Unexpected IV length returned")); + if (iv_len != iv->len) + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Provided IV has incorrect length")); + + apr_err = apr_crypto_block_decrypt_init(&block_ctx, &block_size, + (unsigned char *)iv->data, + key, scratch_pool); + if ((apr_err != APR_SUCCESS) || (! block_ctx)) + return svn_error_trace(crypto_error_create( + ctx, apr_err, + _("Error initializing block decryption"))); + + apr_err = apr_crypto_block_decrypt(NULL, &result_len, + (unsigned char *)ciphertext->data, + ciphertext->len, block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error fetching result length")); + goto cleanup; + } + + result = apr_palloc(scratch_pool, result_len); + apr_err = apr_crypto_block_decrypt(&result, &result_len, + (unsigned char *)ciphertext->data, + ciphertext->len, block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error during block decryption")); + goto cleanup; + } + + apr_err = apr_crypto_block_decrypt_finish(result + result_len, &final_len, + block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error finalizing block decryption")); + goto cleanup; + } + + /* Copy the non-random bits of the resulting plaintext, skipping the + prefix and ignoring any trailing padding. */ + *plaintext = apr_pstrndup(result_pool, + (const char *)(result + RANDOM_PREFIX_LEN), + result_len + final_len - RANDOM_PREFIX_LEN); + + cleanup: + apr_crypto_block_cleanup(block_ctx); + return err; +#else /* SVN_HAVE_CRYPTO */ + return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + "Cryptographic support is not available"); +#endif /* SVN_HAVE_CRYPTO */ +} + + +svn_error_t * +svn_crypto__generate_secret_checktext(const svn_string_t **ciphertext, + const svn_string_t **iv, + const svn_string_t **salt, + const char **checktext, + svn_crypto__ctx_t *ctx, + const svn_string_t *master, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ +#ifdef SVN_HAVE_CRYPTO + svn_error_t *err = SVN_NO_ERROR; + const unsigned char *salt_vector; + const unsigned char *iv_vector; + const unsigned char *stuff_vector; + apr_size_t iv_len; + apr_crypto_key_t *key = NULL; + apr_status_t apr_err; + apr_crypto_block_t *block_ctx = NULL; + apr_size_t block_size; + apr_size_t result_len; + unsigned char *result; + apr_size_t ignored_result_len = 0; + apr_size_t stuff_len; + svn_checksum_t *stuff_sum; + + SVN_ERR_ASSERT(ctx != NULL); + + /* Generate the salt. */ + SVN_ERR(get_random_bytes(&salt_vector, ctx, SALT_LEN, result_pool)); + + /* Initialize the passphrase. */ + apr_err = apr_crypto_passphrase(&key, &iv_len, + master->data, master->len, + salt_vector, SALT_LEN, + APR_KEY_AES_256, APR_MODE_CBC, + FALSE /* doPad */, NUM_ITERATIONS, + ctx->crypto, + scratch_pool); + if (apr_err != APR_SUCCESS) + return svn_error_trace(crypto_error_create( + ctx, apr_err, + _("Error creating derived key"))); + if (! key) + return svn_error_create(APR_EGENERAL, NULL, + _("Error creating derived key")); + if (iv_len == 0) + return svn_error_create(APR_EGENERAL, NULL, + _("Unexpected IV length returned")); + + /* Generate the proper length IV. */ + SVN_ERR(get_random_bytes(&iv_vector, ctx, iv_len, result_pool)); + + /* Initialize block encryption. */ + apr_err = apr_crypto_block_encrypt_init(&block_ctx, &iv_vector, key, + &block_size, scratch_pool); + if ((apr_err != APR_SUCCESS) || (! block_ctx)) + return svn_error_trace(crypto_error_create( + ctx, apr_err, + _("Error initializing block encryption"))); + + /* Generate a blob of random data, block-aligned per the + requirements of the encryption algorithm, but with a minimum size + of our choosing. */ +#define MIN_STUFF_LEN 32 + if (MIN_STUFF_LEN % block_size) + stuff_len = MIN_STUFF_LEN + (block_size - (MIN_STUFF_LEN % block_size)); + else + stuff_len = MIN_STUFF_LEN; + SVN_ERR(get_random_bytes(&stuff_vector, ctx, stuff_len, scratch_pool)); + + /* ### FIXME: This should be a SHA-256. */ + SVN_ERR(svn_checksum(&stuff_sum, svn_checksum_sha1, stuff_vector, + stuff_len, scratch_pool)); + + /* Get the length that we need to allocate. */ + apr_err = apr_crypto_block_encrypt(NULL, &result_len, stuff_vector, + stuff_len, block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error fetching result length")); + goto cleanup; + } + + /* Allocate our result buffer. */ + result = apr_palloc(result_pool, result_len); + + /* Encrypt the block. */ + apr_err = apr_crypto_block_encrypt(&result, &result_len, stuff_vector, + stuff_len, block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error during block encryption")); + goto cleanup; + } + + /* Finalize the block encryption. Since we padded everything, this should + not produce any more encrypted output. */ + apr_err = apr_crypto_block_encrypt_finish(NULL, + &ignored_result_len, + block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error finalizing block encryption")); + goto cleanup; + } + + *ciphertext = wrap_as_string(result, result_len, result_pool); + *iv = wrap_as_string(iv_vector, iv_len, result_pool); + *salt = wrap_as_string(salt_vector, SALT_LEN, result_pool); + *checktext = svn_checksum_to_cstring(stuff_sum, result_pool); + + cleanup: + apr_crypto_block_cleanup(block_ctx); + return err; +#else /* SVN_HAVE_CRYPTO */ + return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + "Cryptographic support is not available"); +#endif /* SVN_HAVE_CRYPTO */ +} + + +svn_error_t * +svn_crypto__verify_secret(svn_boolean_t *is_valid, + svn_crypto__ctx_t *ctx, + const svn_string_t *master, + const svn_string_t *ciphertext, + const svn_string_t *iv, + const svn_string_t *salt, + const char *checktext, + apr_pool_t *scratch_pool) +{ +#ifdef SVN_HAVE_CRYPTO + svn_error_t *err = SVN_NO_ERROR; + apr_status_t apr_err; + apr_crypto_block_t *block_ctx = NULL; + apr_size_t block_size, iv_len; + apr_crypto_key_t *key = NULL; + unsigned char *result; + apr_size_t result_len = 0, final_len = 0; + svn_checksum_t *result_sum; + + *is_valid = FALSE; + + /* Initialize the passphrase. */ + apr_err = apr_crypto_passphrase(&key, &iv_len, + master->data, master->len, + (unsigned char *)salt->data, salt->len, + APR_KEY_AES_256, APR_MODE_CBC, + FALSE /* doPad */, NUM_ITERATIONS, + ctx->crypto, scratch_pool); + if (apr_err != APR_SUCCESS) + return svn_error_trace(crypto_error_create( + ctx, apr_err, + _("Error creating derived key"))); + if (! key) + return svn_error_create(APR_EGENERAL, NULL, + _("Error creating derived key")); + if (iv_len == 0) + return svn_error_create(APR_EGENERAL, NULL, + _("Unexpected IV length returned")); + if (iv_len != iv->len) + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Provided IV has incorrect length")); + + apr_err = apr_crypto_block_decrypt_init(&block_ctx, &block_size, + (unsigned char *)iv->data, + key, scratch_pool); + if ((apr_err != APR_SUCCESS) || (! block_ctx)) + return svn_error_trace(crypto_error_create( + ctx, apr_err, + _("Error initializing block decryption"))); + + apr_err = apr_crypto_block_decrypt(NULL, &result_len, + (unsigned char *)ciphertext->data, + ciphertext->len, block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error fetching result length")); + goto cleanup; + } + + result = apr_palloc(scratch_pool, result_len); + apr_err = apr_crypto_block_decrypt(&result, &result_len, + (unsigned char *)ciphertext->data, + ciphertext->len, block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error during block decryption")); + goto cleanup; + } + + apr_err = apr_crypto_block_decrypt_finish(result + result_len, &final_len, + block_ctx); + if (apr_err != APR_SUCCESS) + { + err = crypto_error_create(ctx, apr_err, + _("Error finalizing block decryption")); + goto cleanup; + } + + /* ### FIXME: This should be a SHA-256. */ + SVN_ERR(svn_checksum(&result_sum, svn_checksum_sha1, result, + result_len + final_len, scratch_pool)); + + *is_valid = strcmp(checktext, + svn_checksum_to_cstring(result_sum, scratch_pool)) == 0; + + cleanup: + apr_crypto_block_cleanup(block_ctx); + return err; +#else /* SVN_HAVE_CRYPTO */ + *is_valid = FALSE; + return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + "Cryptographic support is not available"); +#endif /* SVN_HAVE_CRYPTO */ +} |