subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cmpil...@apache.org
Subject svn commit: r1325441 - in /subversion/trunk/subversion: libsvn_subr/crypto.c libsvn_subr/crypto.h tests/libsvn_subr/crypto-test.c
Date Thu, 12 Apr 2012 18:46:16 GMT
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
   };



Mime
View raw message