Author: cmpilato
Date: Thu Apr 12 18:46:15 2012
New Revision: 1325441
URL: http://svn.apache.org/viewvc?rev=1325441&view=rev
Log:
Implement the bit of the MasterPassphrase design plan which involves
generating and storing validation bits for master passphrases.
* subversion/libsvn_subr/crypto.h,
* subversion/libsvn_subr/crypto.c
(svn_crypto__generate_secret_checktext, svn_crypto__verify_secret):
New functions. (These are most copy-and-pastes of other functions.
I'll made a second pass at abstracting out the common bits out soon.)
* subversion/tests/libsvn_subr/crypto-test.c
(test_passphrase_check): New test function.
(test_funcs): Add reference to new test.
Modified:
subversion/trunk/subversion/libsvn_subr/crypto.c
subversion/trunk/subversion/libsvn_subr/crypto.h
subversion/trunk/subversion/tests/libsvn_subr/crypto-test.c
Modified: subversion/trunk/subversion/libsvn_subr/crypto.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/crypto.c?rev=1325441&r1=1325440&r2=1325441&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/crypto.c (original)
+++ subversion/trunk/subversion/libsvn_subr/crypto.c Thu Apr 12 18:46:15 2012
@@ -29,6 +29,7 @@
#endif /* SVN_HAVE_CRYPTO */
#include "svn_types.h"
+#include "svn_checksum.h"
#include "svn_private_config.h"
#include "private/svn_atomic.h"
@@ -478,3 +479,220 @@ svn_crypto__decrypt_password(const char
"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;
+ svn_checksum_t *stuff_sum;
+
+ SVN_ERR_ASSERT(ctx != NULL);
+
+ /* Generate 32 bytes of random stuff. */
+#define STUFF_LEN 32
+ 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));
+
+ /* 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")));
+
+ /* 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 */
+}
Modified: subversion/trunk/subversion/libsvn_subr/crypto.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/crypto.h?rev=1325441&r1=1325440&r2=1325441&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/crypto.h (original)
+++ subversion/trunk/subversion/libsvn_subr/crypto.h Thu Apr 12 18:46:15 2012
@@ -95,6 +95,45 @@ svn_crypto__decrypt_password(const char
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+/* Generate the stuff Subversion needs to store in order to validate a
+ user-provided MASTER password:
+
+ Set *CIPHERTEXT to a block of encrypted data.
+
+ Set *IV and *SALT to the initialization vector and salt used for
+ encryption.
+
+ Set *CHECKTEXT to the check text used for validation.
+
+ CTX is a Subversion cryptographic context. MASTER is the
+ encryption secret.
+*/
+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);
+
+/* Set *IS_VALID to TRUE iff the encryption secret MASTER successfully
+ validates using Subversion cryptographic context CTX against
+ CIPHERTEXT, IV, SALT, and CHECKTEXT (which where probably generated
+ via previous call to svn_crypto__generate_secret_checktext()).
+
+ Use SCRATCH_POOL for necessary allocations. */
+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 __cplusplus
}
#endif /* __cplusplus */
Modified: subversion/trunk/subversion/tests/libsvn_subr/crypto-test.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/crypto-test.c?rev=1325441&r1=1325440&r2=1325441&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_subr/crypto-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/crypto-test.c Thu Apr 12
18:46:15 2012
@@ -104,6 +104,67 @@ test_encrypt_decrypt_password(apr_pool_t
}
+static svn_error_t *
+test_passphrase_check(apr_pool_t *pool)
+{
+ svn_crypto__ctx_t *ctx;
+ int i;
+ apr_pool_t *iterpool;
+ const char *passwords[] = {
+ "3ncryptm!3", /* fits in one block */
+ "this is a particularly long password", /* spans blocks */
+ "mypassphrase", /* with 4-byte padding, should align on block boundary */
+ };
+ const svn_string_t *ciphertext, *iv, *salt, *secret;
+ const char *checktext;
+ svn_boolean_t is_valid;
+ int num_passwords = sizeof(passwords) / sizeof(const char *);
+
+ /* Skip this test if the crypto subsystem is unavailable. */
+ if (! svn_crypto__is_available())
+ return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, NULL);
+
+ SVN_ERR(svn_crypto__context_create(&ctx, pool));
+
+ iterpool = svn_pool_create(pool);
+ for (i = 0; i < num_passwords; i++)
+ {
+ svn_pool_clear(iterpool);
+ secret = svn_string_create(passwords[i], iterpool);
+ SVN_ERR(svn_crypto__generate_secret_checktext(&ciphertext, &iv, &salt,
+ &checktext, ctx, secret,
+ iterpool, iterpool));
+ SVN_ERR(svn_crypto__verify_secret(&is_valid, ctx, secret, ciphertext,
+ iv, salt, checktext, iterpool));
+ if (! is_valid)
+ return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
+ "Error validating secret against checktext");
+ }
+
+ for (i = 0; i < num_passwords; i++)
+ {
+ int test_secret_index = (i + 1) % num_passwords;
+
+ svn_pool_clear(iterpool);
+ secret = svn_string_create(passwords[i], iterpool);
+ SVN_ERR(svn_crypto__generate_secret_checktext(&ciphertext, &iv, &salt,
+ &checktext, ctx, secret,
+ iterpool, iterpool));
+ secret = svn_string_create(passwords[test_secret_index], iterpool);
+ SVN_ERR(svn_crypto__verify_secret(&is_valid, ctx, secret, ciphertext,
+ iv, salt, checktext, iterpool));
+ if (is_valid)
+ return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
+ "Expected secret validation failure; "
+ "got success");
+ }
+
+ /* Now check that a bogus secret causes the validation to fail. */
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
@@ -114,5 +175,7 @@ struct svn_test_descriptor_t test_funcs[
SVN_TEST_NULL,
SVN_TEST_PASS2(test_encrypt_decrypt_password,
"basic password encryption/decryption test"),
+ SVN_TEST_PASS2(test_passphrase_check,
+ "password checktext generation/validation"),
SVN_TEST_NULL
};