/** * Encrypt the string given as per the current config. * * Returns APR_SUCCESS if successful. */ static apr_status_t encrypt_string(request_rec * r, const apr_crypto_t *f, session_crypto_dir_conf *dconf, const char *in, char **out) { apr_status_t res; apr_crypto_key_t *key = NULL; apr_size_t ivSize = 0; apr_crypto_block_t *block = NULL; unsigned char *encrypt = NULL; unsigned char *combined = NULL; apr_size_t encryptlen, tlen; char *base64; apr_size_t blockSize = 0; const unsigned char *iv = NULL; apr_uuid_t salt; apr_crypto_block_key_type_e *cipher; const char *passphrase; /* by default, return an empty string */ *out = ""; /* don't attempt to encrypt an empty string, trying to do so causes a segfault */ if (!in || !*in) { return APR_SUCCESS; } /* use a uuid as a salt value, and prepend it to our result */ apr_uuid_get(&salt); res = crypt_init(r, f, &cipher, dconf); if (res != APR_SUCCESS) { return res; } /* encrypt using the first passphrase in the list */ passphrase = APR_ARRAY_IDX(dconf->passphrases, 0, char *); res = apr_crypto_passphrase(&key, &ivSize, passphrase, strlen(passphrase), (unsigned char *) (&salt), sizeof(apr_uuid_t), *cipher, APR_MODE_CBC, 1, 4096, f, r->pool); if (APR_STATUS_IS_ENOKEY(res)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01825) "the passphrase '%s' was empty", passphrase); } if (APR_STATUS_IS_EPADDING(res)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01826) "padding is not supported for cipher"); } if (APR_STATUS_IS_EKEYTYPE(res)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01827) "the key type is not known"); } if (APR_SUCCESS != res) { ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01828) "encryption could not be configured."); return res; } res = apr_crypto_block_encrypt_init(&block, &iv, key, &blockSize, r->pool); if (APR_SUCCESS != res) { ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01829) "apr_crypto_block_encrypt_init failed"); return res; } /* encrypt the given string */ res = apr_crypto_block_encrypt(&encrypt, &encryptlen, (unsigned char *)in, strlen(in), block); if (APR_SUCCESS != res) { ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01830) "apr_crypto_block_encrypt failed"); return res; } res = apr_crypto_block_encrypt_finish(encrypt + encryptlen, &tlen, block); if (APR_SUCCESS != res) { ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01831) "apr_crypto_block_encrypt_finish failed"); return res; } encryptlen += tlen; /* prepend the salt and the iv to the result */ combined = apr_palloc(r->pool, ivSize + encryptlen + sizeof(apr_uuid_t)); memcpy(combined, &salt, sizeof(apr_uuid_t)); memcpy(combined + sizeof(apr_uuid_t), iv, ivSize); memcpy(combined + sizeof(apr_uuid_t) + ivSize, encrypt, encryptlen); /* base64 encode the result */ base64 = apr_palloc(r->pool, apr_base64_encode_len(ivSize + encryptlen + sizeof(apr_uuid_t) + 1) * sizeof(char)); apr_base64_encode(base64, (const char *) combined, ivSize + encryptlen + sizeof(apr_uuid_t)); *out = base64; return res; }
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 */ }
static unsigned char *encrypt_block(abts_case *tc, apr_pool_t *pool, const apr_crypto_driver_t *driver, const apr_crypto_t *f, const apr_crypto_key_t *key, const unsigned char *in, const apr_size_t inlen, unsigned char **cipherText, apr_size_t *cipherTextLen, const unsigned char **iv, apr_size_t *blockSize, const char *description) { apr_crypto_block_t *block = NULL; const apu_err_t *result = NULL; apr_size_t len = 0; apr_status_t rv; if (!driver || !f || !key || !in) { return NULL; } /* init the encryption */ rv = apr_crypto_block_encrypt_init(pool, f, key, iv, &block, blockSize); if (APR_ENOTIMPL == rv) { ABTS_NOT_IMPL(tc, "apr_crypto_block_encrypt_init returned APR_ENOTIMPL"); } else { if (APR_SUCCESS != rv) { apr_crypto_error(f, &result); fprintf(stderr, "encrypt_init: %s %s native error %d: %s (%s)\n", description, apr_crypto_driver_name(driver), result->rc, result->reason ? result->reason : "", result->msg ? result->msg : ""); } ABTS_ASSERT(tc, "apr_crypto_block_encrypt_init returned APR_ENOKEY", rv != APR_ENOKEY); ABTS_ASSERT(tc, "apr_crypto_block_encrypt_init returned APR_ENOIV", rv != APR_ENOIV); ABTS_ASSERT(tc, "apr_crypto_block_encrypt_init returned APR_EKEYTYPE", rv != APR_EKEYTYPE); ABTS_ASSERT(tc, "apr_crypto_block_encrypt_init returned APR_EKEYLENGTH", rv != APR_EKEYLENGTH); ABTS_ASSERT(tc, "failed to apr_crypto_block_encrypt_init", rv == APR_SUCCESS); ABTS_ASSERT(tc, "apr_crypto_block_encrypt_init returned NULL context", block != NULL); } if (!block || rv) { return NULL; } /* encrypt the block */ rv = apr_crypto_block_encrypt(f, block, cipherText, cipherTextLen, in, inlen); if (APR_SUCCESS != rv) { apr_crypto_error(f, &result); fprintf(stderr, "encrypt: %s %s native error %d: %s (%s)\n", description, apr_crypto_driver_name(driver), result->rc, result->reason ? result->reason : "", result->msg ? result->msg : ""); } ABTS_ASSERT(tc, "apr_crypto_block_encrypt returned APR_ECRYPT", rv != APR_ECRYPT); ABTS_ASSERT(tc, "failed to apr_crypto_block_encrypt", rv == APR_SUCCESS); ABTS_ASSERT(tc, "apr_crypto_block_encrypt failed to allocate buffer", *cipherText != NULL); if (rv) { return NULL; } /* finalise the encryption */ rv = apr_crypto_block_encrypt_finish(f, block, *cipherText + *cipherTextLen, &len); if (APR_SUCCESS != rv) { apr_crypto_error(f, &result); fprintf(stderr, "encrypt_finish: %s %s native error %d: %s (%s)\n", description, apr_crypto_driver_name(driver), result->rc, result->reason ? result->reason : "", result->msg ? result->msg : ""); } ABTS_ASSERT(tc, "apr_crypto_block_encrypt_finish returned APR_ECRYPT", rv != APR_ECRYPT); ABTS_ASSERT(tc, "apr_crypto_block_encrypt_finish returned APR_EPADDING", rv != APR_EPADDING); ABTS_ASSERT(tc, "failed to apr_crypto_block_encrypt_finish", rv == APR_SUCCESS); *cipherTextLen += len; apr_crypto_block_cleanup(f, block); if (rv) { return NULL; } return *cipherText; }
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 */ }