[prev in list] [next in list] [prev in thread] [next in thread] 

List:       subversion-commits
Subject:    svn commit: r1307940 - in /subversion/trunk/subversion/libsvn_subr: crypto.c crypto.h
From:       gstein () apache ! org
Date:       2012-03-31 23:34:14
Message-ID: 20120331233414.83B92238890B () eris ! apache ! org
[Download RAW message or body]

Author: gstein
Date: Sat Mar 31 23:34:14 2012
New Revision: 1307940

URL: http://svn.apache.org/viewvc?rev=1307940&view=rev
Log:
Revamp the signatures: these functions are designed specifically for
password encryption using a master password. They are not arbitrary
crypto functions, so don't pretend they are.

* subversion/libsvn_subr/crypto.h:
  (svn_crypto__encrypt_cstring): renamed to ...
  (svn_crypto__encrypt_password): ... this. many types adjusted.
  (svn_crypto__decrypt_cstring): renamed to ...
  (svn_crypto__decrypt_password): ... this. many types adjusted.

* subversion/libsvn_subr/crypto.c:
  (NUM_ITERATIONS): constant for our PBKDF2 usage
  (get_random_bytes): constify the output, and make them unsigned char
  (wrap_as_string): helper to alloc an svn_string_t and initialize it
  (svn_crypto__encrypt_cstring): renamed to ...
  (svn_crypto__encrypt_password): ... this. adjust the implementation
    for the new svn_string_t parameters. turn off padding (as this can
    cause a decrypt error, per the OpenSSL docs); we'll manually pad.
    other adjustments and tweaks for our APR crypto calls.
  (svn_crypto__decrypt_cstring): renamed to ...
  (svn_crypto__decrypt_password): ... this

Modified:
    subversion/trunk/subversion/libsvn_subr/crypto.c
    subversion/trunk/subversion/libsvn_subr/crypto.h

Modified: subversion/trunk/subversion/libsvn_subr/crypto.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/crypto.c?rev=1307940&r1=1307939&r2=1307940&view=diff
 ==============================================================================
--- subversion/trunk/subversion/libsvn_subr/crypto.c (original)
+++ subversion/trunk/subversion/libsvn_subr/crypto.c Sat Mar 31 23:34:14 2012
@@ -33,6 +33,9 @@
 #include "private/svn_atomic.h"
 
 
+/* 1000 iterations is the recommended minimum, per RFC 2898, section 4.2.  */
+#define NUM_ITERATIONS 1000
+
 struct svn_crypto__ctx_t {
   apr_crypto_t *crypto;
 
@@ -113,20 +116,22 @@ crypto_error_create(svn_crypto__ctx_t *c
 
 
 static svn_error_t *
-get_random_bytes(void **rand_bytes,
+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;
 
   /* ### need to check APR_HAS_RANDOM  */
 
-  *rand_bytes = apr_palloc(result_pool, rand_len);
-  apr_err = apr_generate_random_bytes(*rand_bytes, rand_len);
+  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;
 }
 
@@ -179,76 +184,127 @@ svn_crypto__context_create(svn_crypto__c
 }
 
 
+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;
+}
+
+
 svn_error_t *
-svn_crypto__encrypt_cstring(unsigned char **ciphertext,
-                            apr_size_t *ciphertext_len,
-                            const unsigned char **iv,
-                            apr_size_t *iv_len,
-                            const unsigned char **salt,
-                            apr_size_t *salt_len,
-                            svn_crypto__ctx_t *ctx,
-                            const char *plaintext,
-                            const char *secret,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool)
+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)
 {
   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 = 0, encrypted_len = 0;
- 
+  apr_size_t block_size;
+  svn_string_t *assembled;
+  apr_size_t result_len;
+  unsigned char *result;
+  apr_size_t ignored_result_len = 0;
+
   SVN_ERR_ASSERT(ctx != NULL);
 
   /* Generate the salt. */
-  *salt_len = 8;
-  SVN_ERR(get_random_bytes((void **)salt, ctx, *salt_len, result_pool));
+#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, NULL, secret, strlen(secret),
-                                  *salt, 8 /* salt_len */,
+  apr_err = apr_crypto_passphrase(&key, &iv_len,
+                                  master->data, master->len,
+                                  salt_vector, SALT_LEN,
                                   APR_KEY_AES_256, APR_MODE_CBC,
-                                  1 /* doPad */, 4096, ctx->crypto,
+                                  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")));
 
+  /* Generate the proper length IV.  */
+  SVN_ERR(get_random_bytes(&iv_vector, ctx, iv_len, result_pool));
+
   /* Generate a 4-byte prefix. */
-  SVN_ERR(get_random_bytes((void **)&prefix, ctx, 4, scratch_pool));
+#define PREFIX_LEN 4
+  SVN_ERR(get_random_bytes(&prefix, ctx, PREFIX_LEN, scratch_pool));
 
   /* Initialize block encryption. */
-  apr_err = apr_crypto_block_encrypt_init(&block_ctx, iv, key, &block_size,
-                                          result_pool);
+  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")));
 
-  /* ### FIXME:  We need to actually use the prefix! */
+  /* ### We need to actually use the prefix, and add appropriate pad  */
+  assembled = svn_string_create(password, scratch_pool);
+
+  /* Get the length that we need to allocate.  */
+  apr_err = apr_crypto_block_encrypt(NULL, &result_len,
+                                     (unsigned char *)assembled->data,
+                                     assembled->len,
+                                     block_ctx);
+  if (apr_err != APR_SUCCESS)
+    {
+      err = crypto_error_create(ctx, apr_err,
+                                _("Error fetching result length"));
+      goto cleanup;
+    }
+
+  /* The result length should precisely match our padded input. If not,
+     then something weird is going on with the crypto libraries.  */
+  SVN_ERR_ASSERT(result_len == assembled->len);
+
+  /* Allocate our result buffer.  */
+  result = apr_palloc(result_pool, result_len);
 
   /* Encrypt the block. */
-  apr_err = apr_crypto_block_encrypt(ciphertext, ciphertext_len,
-                                     (unsigned char *)plaintext,
-                                     strlen(plaintext) + 1, block_ctx);
+  apr_err = apr_crypto_block_encrypt(&result, &result_len,
+                                     (unsigned char *)assembled->data,
+                                     assembled->len,
+                                     block_ctx);
   if (apr_err != APR_SUCCESS)
     {
-      err = crypto_error_create(ctx, apr_err, _("Error encrypting block"));
+      err = crypto_error_create(ctx, apr_err,
+                                _("Error during block encryption"));
       goto cleanup;
     }
 
-  /* Finalize the block encryption. */
-  apr_err = apr_crypto_block_encrypt_finish(*ciphertext + *ciphertext_len,
-                                            &encrypted_len, block_ctx);
+  /* 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;
@@ -256,17 +312,14 @@ svn_crypto__encrypt_cstring(unsigned cha
 
 
 svn_error_t *
-svn_crypto__decrypt_cstring(const svn_string_t **plaintext,
-                            svn_crypto__ctx_t *ctx,
-                            const unsigned char *ciphertext,
-                            apr_size_t ciphertext_len,
-                            const unsigned char *iv,
-                            apr_size_t iv_len,
-                            const unsigned char *salt,
-                            apr_size_t salt_len,
-                            const svn_string_t *secret,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool)
+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)
 {
   return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
 }

Modified: subversion/trunk/subversion/libsvn_subr/crypto.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/crypto.h?rev=1307940&r1=1307939&r2=1307940&view=diff
 ==============================================================================
--- subversion/trunk/subversion/libsvn_subr/crypto.h (original)
+++ subversion/trunk/subversion/libsvn_subr/crypto.h Sat Mar 31 23:34:14 2012
@@ -50,32 +50,40 @@ svn_crypto__context_create(svn_crypto__c
                            apr_pool_t *result_pool);
 
 
+/* Using a PBKDF2 derivative key based on MASTER, encrypt PLAINTEXT.
+   The salt used for PBKDF2 is returned in SALT, and the IV used for
+   the (AES-256/CBC) encryption is returned in IV. The resulting
+   encrypted data is returned in CIPHERTEXT.
+
+   Note that MASTER may be the plaintext obtained from the user or
+   some other OS-provided cryptographic store, or it can be a derivation
+   such as SHA1(plaintext). As long as the same octets are passed to
+   the decryption function, everything works just fine. (the SHA1
+   approach is suggested, to avoid keeping the plaintext master in
+   the process' memory space)  */
 svn_error_t *
-svn_crypto__encrypt_cstring(unsigned char **ciphertext,
-                            apr_size_t *ciphertext_len,
-                            const unsigned char **iv,
-                            apr_size_t *iv_len,
-                            const unsigned char **salt,
-                            apr_size_t *salt_len,
-                            svn_crypto__ctx_t *ctx,
-                            const char *plaintext,
-                            const char *secret,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool);
+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 *plaintext,
+                             const svn_string_t *master,
+                             apr_pool_t *result_pool,
+                             apr_pool_t *scratch_pool);
 
 
+/* Given the CIPHERTEXT which was encrypted using (AES-256/CBC) with
+   initialization vector given by IV, and a key derived using PBKDF2
+   with SALT and MASTER... return the decrypted password in PLAINTEXT.  */
 svn_error_t *
-svn_crypto__decrypt_cstring(const svn_string_t **plaintext,
-                            svn_crypto__ctx_t *ctx,
-                            const unsigned char *ciphertext,
-                            apr_size_t ciphertext_len,
-                            const unsigned char *iv,
-                            apr_size_t iv_len,
-                            const unsigned char *salt,
-                            apr_size_t salt_len,
-                            const svn_string_t *secret,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool);
+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 __cplusplus
 }


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic