summaryrefslogtreecommitdiff
path: root/subversion/libsvn_subr/crypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_subr/crypto.c')
-rw-r--r--subversion/libsvn_subr/crypto.c705
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 0000000..f3611a1
--- /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 */
+}