cmpil...@apache.org wrote on Fri, Mar 30, 2012 at 17:12:37 -0000:
> Author: cmpilato
> Date: Fri Mar 30 17:12:36 2012
> New Revision: 1307538
> 
> URL: http://svn.apache.org/viewvc?rev=1307538&view=rev
> Log:
> For issue #4145 ("Master passphrase and encrypted credentials cache"),
> begin adding support for some cryptographic routines in Subversion.
> 
> NOTE:  The code thus far is no where near complete, but I want to get
> this into version control sooner rather than later.
> 
> * subversion/libsvn_subr/crypto.h,
> * subversion/libsvn_subr/crypto.c
>   New files, with incomplete and as-yet-unused functions in them.
> 
> * subversion/svn/main.c
>   (crypto_init): New function.
>   (main): Call crypto_init().
> 
> * build.conf
>   (crypto-test): New section (for a new test binary, also added to
>     other relevant bits of this configuration file.
> 
> * subversion/tests/libsvn_subr
>   Add 'crypto-test' to svn:ignores.
> 
> * subversion/tests/libsvn_subr/crypto-test.c
>   New skeletal test file.
> 
> Added: subversion/trunk/subversion/libsvn_subr/crypto.c
> URL: 
> http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/crypto.c?rev=1307538&view=auto
> ==============================================================================
> --- subversion/trunk/subversion/libsvn_subr/crypto.c (added)
> +++ subversion/trunk/subversion/libsvn_subr/crypto.c Fri Mar 30 17:12:36 2012
> @@ -0,0 +1,193 @@
> +/*
> + * 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.
> + * ====================================================================
> + */
> +
> +#ifdef APU_HAVE_CRYPTO
> +
> +#include "crypto.h"
> +
> +#include <apr_random.h>
> +
> +
> +static svn_error_t *
> +get_random_bytes(void **rand_bytes,
> +                 apr_size_t rand_len,
> +                 apr_pool_t *pool)
> +{
> +  apr_random_t *apr_rand;

Shouldn't you reuse this context as much as possible?  (instead of
creating a new context every time)

> +  apr_status_t apr_err;
> +
> +  *rand_bytes = apr_palloc(pool, rand_len);
> +  apr_rand = apr_random_standard_new(pool);

Can this call block?  I assume it can't, but APR docs aren't explicit.

Do you need to call apr_random_add_entropy() somewhere?  From code
inspection the code will just error out if that function hasn't been
called (that function is the only codepath that sets 'g->secure_started'
to '1').

> +  apr_err = apr_random_secure_bytes(apr_rand, *rand_bytes, rand_len);
> +  if (apr_err != APR_SUCCESS)
> +    return svn_error_create(apr_err, NULL, _("Error obtaining random data"));
> +  
> +  return SVN_NO_ERROR;
> +}
> +
> +svn_error_t *
> +svn_crypto__context_create(apr_crypto_t **crypto_ctx,
> +                           apr_pool_t *pool)
> +{
> +  apr_status_t apr_err;
> +  const apu_err_t *apu_err = NULL;
> +  const apr_crypto_driver_t *driver;
> +
> +  /* ### TODO: So much for abstraction.  APR's wrappings around NSS
> +         and OpenSSL aren't quite as opaque as I'd hoped, requiring us
> +         to specify a driver type and then params to the driver.  We
> +         *could* use APU_CRYPTO_RECOMMENDED_DRIVER for the driver bit,
> +         but we'd still have to then dynamically ask APR which driver
> +         it used and then figure out the parameters to send to that
> +         driver at apr_crypto_make() time.  Or maybe I'm just
> +         overlooking something...   -- cmpilato  */
> +
> +  apr_err = apr_crypto_get_driver(&driver, "openssl", NULL, &apu_err, pool);
> +  if ((apr_err != APR_SUCCESS) || (! driver))
> +    return svn_error_createf(apr_err,
> +                             err_from_apu_err(apr_err, apu_err),

If the local variable APR_ERR has value APR_SUCCESS, you'll be creating
here an svn_error_t object whose APR_ERR member is APR_SUCCESS.  Perhaps
return a more useful error code when (driver == NULL && ! apr_err)?

> +                             _("OpenSSL crypto driver error"));
> +
> +  apr_err = apr_crypto_make(crypto_ctx, driver, "engine=openssl", pool);
> +  if ((apr_err != APR_SUCCESS) || (! *crypto_ctx))
> +    return svn_error_create(apr_err, NULL,
> +                            _("Error creating OpenSSL crypto context"));
> +
> +  return SVN_NO_ERROR;
> +}
> +
> +svn_error_t *
> +svn_crypto__encrypt_cstring(unsigned char **ciphertext,
> +                            apr_size_t *ciphertext_len,

svn_string_t **ciphertext ?

> +                            const unsigned char **iv,
> +                            apr_size_t *iv_len,
> +                            const unsigned char **salt,
> +                            apr_size_t *salt_len,
> +                            apr_crypto_t *crypto_ctx,
> +                            const char *plaintext,
> +                            const char *secret,
> +                            apr_pool_t *result_pool,
> +                            apr_pool_t *scratch_pool)
> +{
> +  svn_error_t *err = SVN_NO_ERROR;
> +  apr_crypto_key_t *key = NULL;
> +  const apu_err_t *apu_err = NULL;

Could move this variable to block scope.

> +  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;
> + 
> +  SVN_ERR_ASSERT(crypto_ctx);
> +
> +  /* Generate the salt. */
> +  *salt_len = 8;
> +  SVN_ERR(get_random_bytes((void **)salt, *salt_len, result_pool));
> +
> +  /* Initialize the passphrase.  */
> +  apr_err = apr_crypto_passphrase(&key, NULL, secret, strlen(secret),
> +                                  *salt, 8 /* salt_len */,
> +                                  APR_KEY_AES_256, APR_MODE_CBC,
> +                                  1 /* doPad */, 4096, crypto_ctx,
> +                                  scratch_pool);
> +  if (apr_err != APR_SUCCESS)
> +    {
> +      apr_crypto_error(&apu_err, crypto_ctx);

Need to check the return value of apr_crypto_error() before
dereferencing APU_ERR in err_from_apu_err())

> +      return svn_error_create(apr_err, err_from_apu_err(apr_err, apu_err),
> +                              _("Error creating derived key"));
> +    }
> +
> +  /* Generate a 4-byte prefix. */
> +  SVN_ERR(get_random_bytes((void **)&prefix, 4, scratch_pool));
> +
> +  /* Initialize block encryption. */
> +  apr_err = apr_crypto_block_encrypt_init(&block_ctx, iv, key, &block_size,
> +                                          result_pool);
> +  if ((apr_err != APR_SUCCESS) || (! block_ctx))
> +    {
> +      apr_crypto_error(&apu_err, crypto_ctx);
> +      return svn_error_create(apr_err, err_from_apu_err(apr_err, apu_err),
> +                              _("Error initializing block encryption"));
> +    }
> +
> +  /* ### FIXME:  We need to actually use the prefix! */

Also, need to avoid adding 1 to strlen() below if it's a multiple of 16.

> +
> +  /* Encrypt the block. */
> +  apr_err = apr_crypto_block_encrypt(ciphertext, ciphertext_len,
> +                                     (unsigned char *)plaintext,
> +                                     strlen(plaintext) + 1, block_ctx);
> +  if (apr_err != APR_SUCCESS)
> +    {
> +      apr_crypto_error(&apu_err, crypto_ctx);
> +      err = svn_error_create(apr_err, err_from_apu_err(apr_err, apu_err),
> +                             _("Error encrypting block"));
> +      goto cleanup;
> +    }
> +
> +  /* Finalize the block encryption. */
> +  apr_err = apr_crypto_block_encrypt_finish(*ciphertext + *ciphertext_len,
> +                                            &encrypted_len, block_ctx);
> +  if (apr_err != APR_SUCCESS)
> +    {
> +      apr_crypto_error(&apu_err, crypto_ctx);
> +      err = svn_error_create(apr_err, err_from_apu_err(apr_err, apu_err),
> +                             _("Error finalizing block encryption"));
> +      goto cleanup;
> +    }
> +  
> + cleanup:
> +  apr_crypto_block_cleanup(block_ctx);
> +  return err;
> +}

Reply via email to