blob: e2fa38b618288206a0d15615eb5142296421ba2c [file] [log] [blame]
/* 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.
*/
#include "apr_lib.h"
#include "apu.h"
#include "apr_private.h"
#include "apu_errno.h"
#include <ctype.h>
#include <assert.h>
#include <stdlib.h>
#include "apr_strings.h"
#include "apr_time.h"
#include "apr_buckets.h"
#include "apr_thread_mutex.h"
#include "apr_crypto_internal.h"
#if APU_HAVE_CRYPTO
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/engine.h>
#include <openssl/crypto.h>
#include <openssl/obj_mac.h> /* for NID_* */
#include <openssl/conf.h>
#include <openssl/comp.h>
#include <openssl/ssl.h>
#define LOG_PREFIX "apr_crypto_openssl: "
#ifndef APR_USE_OPENSSL_PRE_1_1_API
#if defined(LIBRESSL_VERSION_NUMBER)
/* LibreSSL declares OPENSSL_VERSION_NUMBER == 2.0 but does not necessarily
* include changes from OpenSSL >= 1.1 (new functions, macros, * deprecations,
* ...), so we have to work around this...
*/
#define APR_USE_OPENSSL_PRE_1_0_API (0)
#define APR_USE_OPENSSL_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f)
#define APR_USE_OPENSSL_PRE_1_1_1_API (1)
#else /* defined(LIBRESSL_VERSION_NUMBER) */
#define APR_USE_OPENSSL_PRE_1_0_API (OPENSSL_VERSION_NUMBER < 0x10000000L)
#define APR_USE_OPENSSL_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
#define APR_USE_OPENSSL_PRE_1_1_1_API (OPENSSL_VERSION_NUMBER < 0x10101000L)
#endif /* defined(LIBRESSL_VERSION_NUMBER) */
#endif /* ndef APR_USE_OPENSSL_PRE_1_1_API */
struct apr_crypto_t {
apr_pool_t *pool;
const apr_crypto_driver_t *provider;
apu_err_t *result;
apr_crypto_config_t *config;
apr_hash_t *types;
apr_hash_t *modes;
apr_hash_t *digests;
};
struct apr_crypto_config_t {
ENGINE *engine;
};
struct apr_crypto_key_t {
apr_pool_t *pool;
const apr_crypto_driver_t *provider;
const apr_crypto_t *f;
const apr_crypto_key_rec_t *rec;
const EVP_CIPHER *cipher;
const EVP_MD *hmac;
EVP_PKEY *pkey;
unsigned char *key;
int keyLen;
int doPad;
int ivSize;
};
struct apr_crypto_block_t {
apr_pool_t *pool;
const apr_crypto_driver_t *provider;
const apr_crypto_t *f;
const apr_crypto_key_t *key;
EVP_CIPHER_CTX *cipherCtx;
int initialised;
int ivSize;
int blockSize;
int doPad;
};
struct apr_crypto_digest_t {
apr_pool_t *pool;
const apr_crypto_driver_t *provider;
const apr_crypto_t *f;
const apr_crypto_key_t *key;
apr_crypto_digest_rec_t *rec;
EVP_MD_CTX *mdCtx;
int initialised;
int digestSize;
};
struct cprng_stream_ctx_t {
EVP_CIPHER_CTX *ctx;
int malloced;
};
static struct apr_crypto_block_key_digest_t key_digests[] =
{
{ APR_CRYPTO_DIGEST_MD5, 16, 64 },
{ APR_CRYPTO_DIGEST_SHA1, 20, 64 },
{ APR_CRYPTO_DIGEST_SHA224, 28, 64 },
{ APR_CRYPTO_DIGEST_SHA256, 32, 64 },
{ APR_CRYPTO_DIGEST_SHA384, 48, 128 },
{ APR_CRYPTO_DIGEST_SHA512, 64, 128 } };
static struct apr_crypto_block_key_type_t key_types[] =
{
{ APR_KEY_3DES_192, 24, 8, 8 },
{ APR_KEY_AES_128, 16, 16, 16 },
{ APR_KEY_AES_192, 24, 16, 16 },
{ APR_KEY_AES_256, 32, 16, 16 } };
static struct apr_crypto_block_key_mode_t key_modes[] =
{
{ APR_MODE_ECB },
{ APR_MODE_CBC } };
/* sufficient space to wrap a key */
#define BUFFER_SIZE 128
/**
* Fetch the most recent error from this driver.
*/
static apr_status_t crypto_error(const apu_err_t **result,
const apr_crypto_t *f)
{
*result = f->result;
return APR_SUCCESS;
}
/**
* Shutdown the crypto library and release resources.
*/
static apr_status_t crypto_shutdown(void)
{
#if HAVE_DECL_OPENSSL_INIT_CRYPTO
/* Openssl v1.1+ handles all termination automatically. Do
* nothing in this case.
*/
#else
/* Termination below is for legacy Openssl versions v1.0.x and
* older.
*/
ERR_free_strings();
EVP_cleanup();
ENGINE_cleanup();
#endif
return APR_SUCCESS;
}
static apr_status_t crypto_shutdown_helper(void *data)
{
return crypto_shutdown();
}
/**
* Initialise the crypto library and perform one time initialisation.
*/
static apr_status_t crypto_init(apr_pool_t *pool, const char *params,
const apu_err_t **result)
{
#if HAVE_DECL_OPENSSL_INIT_CRYPTO
/* Openssl v1.1+ handles all initialisation automatically, apart
* from hints as to how we want to use the library.
*
* We tell openssl we want to include engine support.
*/
OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
#else
/* Configuration below is for legacy versions Openssl v1.0 and
* older.
*/
#if APR_USE_OPENSSL_PRE_1_1_API
(void)CRYPTO_malloc_init();
#else
OPENSSL_malloc_init();
#endif
ERR_load_crypto_strings();
/* SSL_load_error_strings(); */
OpenSSL_add_all_algorithms();
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
#endif
apr_pool_cleanup_register(pool, pool, crypto_shutdown_helper,
apr_pool_cleanup_null);
return APR_SUCCESS;
}
#if OPENSSL_VERSION_NUMBER < 0x0090802fL
/* Code taken from OpenSSL 0.9.8b, see
* https://github.com/openssl/openssl/commit/cf6bc84148cb15af09b292394aaf2b45f0d5af0d
*/
EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void)
{
EVP_CIPHER_CTX *ctx = OPENSSL_malloc(sizeof *ctx);
if (ctx)
EVP_CIPHER_CTX_init(ctx);
return ctx;
}
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx)
{
if (ctx) {
EVP_CIPHER_CTX_cleanup(ctx);
OPENSSL_free(ctx);
}
}
#endif
#if APR_USE_OPENSSL_PRE_1_1_API
#define EVP_MD_CTX_new EVP_MD_CTX_create
#define EVP_MD_CTX_free EVP_MD_CTX_destroy
#endif
/**
* @brief Clean key.
* @param key The key to use.
* @return Returns APR_ENOTIMPL if not supported.
*/
static apr_status_t crypto_key_cleanup(apr_crypto_key_t *key)
{
if (key->pkey) {
EVP_PKEY_free(key->pkey);
}
return APR_SUCCESS;
}
static apr_status_t crypto_key_cleanup_helper(void *data)
{
apr_crypto_key_t *key = (apr_crypto_key_t *) data;
return crypto_key_cleanup(key);
}
/**
* @brief Clean encryption / decryption context.
* @note After cleanup, a context is free to be reused if necessary.
* @param ctx The block context to use.
* @return Returns APR_ENOTIMPL if not supported.
*/
static apr_status_t crypto_block_cleanup(apr_crypto_block_t *ctx)
{
if (ctx->initialised) {
if (ctx->cipherCtx) {
#if APR_USE_OPENSSL_PRE_1_1_API
EVP_CIPHER_CTX_cleanup(ctx->cipherCtx);
#else
EVP_CIPHER_CTX_reset(ctx->cipherCtx);
EVP_CIPHER_CTX_free(ctx->cipherCtx);
#endif
ctx->cipherCtx = NULL;
}
ctx->initialised = 0;
}
return APR_SUCCESS;
}
static apr_status_t crypto_block_cleanup_helper(void *data)
{
apr_crypto_block_t *block = (apr_crypto_block_t *) data;
return crypto_block_cleanup(block);
}
/**
* @brief Clean sign / verify context.
* @note After cleanup, a context is free to be reused if necessary.
* @param ctx The block context to use.
* @return Returns APR_ENOTIMPL if not supported.
*/
static apr_status_t crypto_digest_cleanup(apr_crypto_digest_t *ctx)
{
if (ctx->initialised) {
if (ctx->mdCtx) {
EVP_MD_CTX_free(ctx->mdCtx);
ctx->mdCtx = NULL;
}
ctx->initialised = 0;
}
return APR_SUCCESS;
}
static apr_status_t crypto_digest_cleanup_helper(void *data)
{
apr_crypto_digest_t *digest = (apr_crypto_digest_t *) data;
return crypto_digest_cleanup(digest);
}
/**
* @brief Clean encryption / decryption context.
* @note After cleanup, a context is free to be reused if necessary.
* @param f The context to use.
* @return Returns APR_ENOTIMPL if not supported.
*/
static apr_status_t crypto_cleanup(apr_crypto_t *f)
{
if (f->config->engine) {
ENGINE_finish(f->config->engine);
ENGINE_free(f->config->engine);
f->config->engine = NULL;
}
return APR_SUCCESS;
}
static apr_status_t crypto_cleanup_helper(void *data)
{
apr_crypto_t *f = (apr_crypto_t *) data;
return crypto_cleanup(f);
}
/**
* @brief Create a context for supporting encryption. Keys, certificates,
* algorithms and other parameters will be set per context. More than
* one context can be created at one time. A cleanup will be automatically
* registered with the given pool to guarantee a graceful shutdown.
* @param f - context pointer will be written here
* @param provider - provider to use
* @param params - array of key parameters
* @param pool - process pool
* @return APR_ENOENGINE when the engine specified does not exist. APR_EINITENGINE
* if the engine cannot be initialised.
*/
static apr_status_t crypto_make(apr_crypto_t **ff,
const apr_crypto_driver_t *provider, const char *params,
apr_pool_t *pool)
{
apr_crypto_config_t *config = NULL;
apr_crypto_t *f = apr_pcalloc(pool, sizeof(apr_crypto_t));
const char *engine = NULL;
struct {
const char *field;
const char *value;
int set;
} fields[] = {
{ "engine", NULL, 0 },
{ NULL, NULL, 0 }
};
const char *ptr;
size_t klen;
char **elts = NULL;
char *elt;
int i = 0, j;
apr_status_t status;
if (params) {
if (APR_SUCCESS != (status = apr_tokenize_to_argv(params, &elts, pool))) {
return status;
}
while ((elt = elts[i])) {
ptr = strchr(elt, '=');
if (ptr) {
for (klen = ptr - elt; klen && apr_isspace(elt[klen - 1]); --klen)
;
ptr++;
}
else {
for (klen = strlen(elt); klen && apr_isspace(elt[klen - 1]); --klen)
;
}
elt[klen] = 0;
for (j = 0; fields[j].field != NULL; ++j) {
if (!strcasecmp(fields[j].field, elt)) {
fields[j].set = 1;
if (ptr) {
fields[j].value = ptr;
}
break;
}
}
i++;
}
engine = fields[0].value;
}
if (!f) {
return APR_ENOMEM;
}
*ff = f;
f->pool = pool;
f->provider = provider;
config = f->config = apr_pcalloc(pool, sizeof(apr_crypto_config_t));
if (!config) {
return APR_ENOMEM;
}
f->result = apr_pcalloc(pool, sizeof(apu_err_t));
if (!f->result) {
return APR_ENOMEM;
}
f->digests = apr_hash_make(pool);
if (!f->digests) {
return APR_ENOMEM;
}
apr_hash_set(f->digests, "md5", APR_HASH_KEY_STRING, &(key_digests[i = 0]));
apr_hash_set(f->digests, "sha1", APR_HASH_KEY_STRING, &(key_digests[++i]));
apr_hash_set(f->digests, "sha224", APR_HASH_KEY_STRING, &(key_digests[++i]));
apr_hash_set(f->digests, "sha256", APR_HASH_KEY_STRING, &(key_digests[++i]));
apr_hash_set(f->digests, "sha384", APR_HASH_KEY_STRING, &(key_digests[++i]));
apr_hash_set(f->digests, "sha512", APR_HASH_KEY_STRING, &(key_digests[++i]));
f->types = apr_hash_make(pool);
if (!f->types) {
return APR_ENOMEM;
}
apr_hash_set(f->types, "3des192", APR_HASH_KEY_STRING, &(key_types[i = 0]));
apr_hash_set(f->types, "aes128", APR_HASH_KEY_STRING, &(key_types[++i]));
apr_hash_set(f->types, "aes192", APR_HASH_KEY_STRING, &(key_types[++i]));
apr_hash_set(f->types, "aes256", APR_HASH_KEY_STRING, &(key_types[++i]));
f->modes = apr_hash_make(pool);
if (!f->modes) {
return APR_ENOMEM;
}
apr_hash_set(f->modes, "ecb", APR_HASH_KEY_STRING, &(key_modes[i = 0]));
apr_hash_set(f->modes, "cbc", APR_HASH_KEY_STRING, &(key_modes[++i]));
f->digests = apr_hash_make(pool);
if (!f->digests) {
return APR_ENOMEM;
}
apr_pool_cleanup_register(pool, f, crypto_cleanup_helper,
apr_pool_cleanup_null);
if (engine) {
config->engine = ENGINE_by_id(engine);
if (!config->engine) {
return APR_ENOENGINE;
}
if (!ENGINE_init(config->engine)) {
ENGINE_free(config->engine);
config->engine = NULL;
return APR_EINITENGINE;
}
}
return APR_SUCCESS;
}
/**
* @brief Get a hash table of key digests, keyed by the name of the digest against
* a pointer to apr_crypto_block_key_digest_t.
*
* @param digests - hashtable of key digests keyed to constants.
* @param f - encryption context
* @return APR_SUCCESS for success
*/
static apr_status_t crypto_get_block_key_digests(apr_hash_t **digests,
const apr_crypto_t *f)
{
*digests = f->digests;
return APR_SUCCESS;
}
/**
* @brief Get a hash table of key types, keyed by the name of the type against
* a pointer to apr_crypto_block_key_type_t.
*
* @param types - hashtable of key types keyed to constants.
* @param f - encryption context
* @return APR_SUCCESS for success
*/
static apr_status_t crypto_get_block_key_types(apr_hash_t **types,
const apr_crypto_t *f)
{
*types = f->types;
return APR_SUCCESS;
}
/**
* @brief Get a hash table of key modes, keyed by the name of the mode against
* a pointer to apr_crypto_block_key_mode_t.
*
* @param modes - hashtable of key modes keyed to constants.
* @param f - encryption context
* @return APR_SUCCESS for success
*/
static apr_status_t crypto_get_block_key_modes(apr_hash_t **modes,
const apr_crypto_t *f)
{
*modes = f->modes;
return APR_SUCCESS;
}
/*
* Work out which mechanism to use.
*/
static apr_status_t crypto_cipher_mechanism(apr_crypto_key_t *key,
const apr_crypto_block_key_type_e type,
const apr_crypto_block_key_mode_e mode, const int doPad, apr_pool_t *p)
{
/* determine the cipher to be used */
switch (type) {
case (APR_KEY_3DES_192):
/* A 3DES key */
if (mode == APR_MODE_CBC) {
key->cipher = EVP_des_ede3_cbc();
}
else {
key->cipher = EVP_des_ede3_ecb();
}
break;
case (APR_KEY_AES_128):
if (mode == APR_MODE_CBC) {
key->cipher = EVP_aes_128_cbc();
}
else {
key->cipher = EVP_aes_128_ecb();
}
break;
case (APR_KEY_AES_192):
if (mode == APR_MODE_CBC) {
key->cipher = EVP_aes_192_cbc();
}
else {
key->cipher = EVP_aes_192_ecb();
}
break;
case (APR_KEY_AES_256):
if (mode == APR_MODE_CBC) {
key->cipher = EVP_aes_256_cbc();
}
else {
key->cipher = EVP_aes_256_ecb();
}
break;
default:
/* unknown key type, give up */
return APR_EKEYTYPE;
}
/* find the length of the key we need */
key->keyLen = EVP_CIPHER_key_length(key->cipher);
/* make space for the key */
key->key = apr_pcalloc(p, key->keyLen);
if (!key->key) {
return APR_ENOMEM;
}
apr_crypto_clear(p, key->key, key->keyLen);
return APR_SUCCESS;
}
/**
* @brief Create a key from the provided secret or passphrase. The key is cleaned
* up when the context is cleaned, and may be reused with multiple encryption
* or decryption operations.
* @note If *key is NULL, a apr_crypto_key_t will be created from a pool. If
* *key is not NULL, *key must point at a previously created structure.
* @param key The key returned, see note.
* @param rec The key record, from which the key will be derived.
* @param f The context to use.
* @param p The pool to use.
* @return Returns APR_ENOKEY if the pass phrase is missing or empty, or if a backend
* error occurred while generating the key. APR_ENOCIPHER if the type or mode
* is not supported by the particular backend. APR_EKEYTYPE if the key type is
* not known. APR_EPADDING if padding was requested but is not supported.
* APR_ENOTIMPL if not implemented.
*/
static apr_status_t crypto_key(apr_crypto_key_t **k,
const apr_crypto_key_rec_t *rec, const apr_crypto_t *f, apr_pool_t *p)
{
apr_crypto_key_t *key = *k;
apr_status_t rv;
if (!key) {
*k = key = apr_pcalloc(p, sizeof *key);
if (!key) {
return APR_ENOMEM;
}
}
apr_pool_cleanup_register(p, key, crypto_key_cleanup_helper,
apr_pool_cleanup_null);
key->pool = p;
key->f = f;
key->provider = f->provider;
key->rec = rec;
switch (rec->ktype) {
case APR_CRYPTO_KTYPE_PASSPHRASE: {
/* decide on what cipher mechanism we will be using */
rv = crypto_cipher_mechanism(key, rec->type, rec->mode, rec->pad, p);
if (APR_SUCCESS != rv) {
return rv;
}
/* generate the key */
if (PKCS5_PBKDF2_HMAC_SHA1(rec->k.passphrase.pass,
rec->k.passphrase.passLen,
(unsigned char *) rec->k.passphrase.salt,
rec->k.passphrase.saltLen, rec->k.passphrase.iterations,
key->keyLen, key->key) == 0) {
return APR_ENOKEY;
}
break;
}
case APR_CRYPTO_KTYPE_SECRET: {
/* decide on what cipher mechanism we will be using */
rv = crypto_cipher_mechanism(key, rec->type, rec->mode, rec->pad, p);
if (APR_SUCCESS != rv) {
return rv;
}
/* sanity check - key correct size? */
if (rec->k.secret.secretLen != key->keyLen) {
return APR_EKEYLENGTH;
}
/* copy the key */
memcpy(key->key, rec->k.secret.secret, rec->k.secret.secretLen);
break;
}
case APR_CRYPTO_KTYPE_HASH: {
switch (rec->k.hash.digest) {
case APR_CRYPTO_DIGEST_MD5:
key->hmac = EVP_md5();
break;
case APR_CRYPTO_DIGEST_SHA1:
key->hmac = EVP_sha1();
break;
case APR_CRYPTO_DIGEST_SHA224:
key->hmac = EVP_sha224();
break;
case APR_CRYPTO_DIGEST_SHA256:
key->hmac = EVP_sha256();
break;
case APR_CRYPTO_DIGEST_SHA384:
key->hmac = EVP_sha384();
break;
case APR_CRYPTO_DIGEST_SHA512:
key->hmac = EVP_sha512();
break;
default:
return APR_ENODIGEST;
}
break;
}
case APR_CRYPTO_KTYPE_HMAC: {
apr_crypto_config_t *config = f->config;
/* create hmac key */
if (!(key->pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, config->engine,
rec->k.hmac.secret, rec->k.hmac.secretLen))) {
return APR_ENOKEY;
}
switch (rec->k.hmac.digest) {
case APR_CRYPTO_DIGEST_MD5:
key->hmac = EVP_md5();
break;
case APR_CRYPTO_DIGEST_SHA1:
key->hmac = EVP_sha1();
break;
case APR_CRYPTO_DIGEST_SHA224:
key->hmac = EVP_sha224();
break;
case APR_CRYPTO_DIGEST_SHA256:
key->hmac = EVP_sha256();
break;
case APR_CRYPTO_DIGEST_SHA384:
key->hmac = EVP_sha384();
break;
case APR_CRYPTO_DIGEST_SHA512:
key->hmac = EVP_sha512();
break;
default:
return APR_ENODIGEST;
}
break;
}
case APR_CRYPTO_KTYPE_CMAC: {
#if !APR_USE_OPENSSL_PRE_1_1_1_API
apr_crypto_config_t *config = f->config;
/* decide on what cipher mechanism we will be using */
rv = crypto_cipher_mechanism(key, rec->type, rec->mode, rec->pad, p);
if (APR_SUCCESS != rv) {
return rv;
}
/* create cmac key */
if (!(key->pkey = EVP_PKEY_new_CMAC_key(config->engine,
rec->k.cmac.secret, rec->k.cmac.secretLen, key->cipher))) {
return APR_ENOKEY;
}
switch (rec->k.hmac.digest) {
case APR_CRYPTO_DIGEST_MD5:
key->hmac = EVP_md5();
break;
case APR_CRYPTO_DIGEST_SHA1:
key->hmac = EVP_sha1();
break;
case APR_CRYPTO_DIGEST_SHA224:
key->hmac = EVP_sha224();
break;
case APR_CRYPTO_DIGEST_SHA256:
key->hmac = EVP_sha256();
break;
case APR_CRYPTO_DIGEST_SHA384:
key->hmac = EVP_sha384();
break;
case APR_CRYPTO_DIGEST_SHA512:
key->hmac = EVP_sha512();
break;
default:
return APR_ENODIGEST;
}
#else
return APR_ENOTIMPL;
#endif
break;
}
default: {
return APR_ENOKEY;
}
}
key->doPad = rec->pad;
/* note: openssl incorrectly returns non zero IV size values for ECB
* algorithms, so work around this by ignoring the IV size.
*/
if (APR_MODE_ECB != rec->mode && key->cipher) {
key->ivSize = EVP_CIPHER_iv_length(key->cipher);
}
return APR_SUCCESS;
}
/**
* @brief Create a key from the given passphrase. By default, the PBKDF2
* algorithm is used to generate the key from the passphrase. It is expected
* that the same pass phrase will generate the same key, regardless of the
* backend crypto platform used. The key is cleaned up when the context
* is cleaned, and may be reused with multiple encryption or decryption
* operations.
* @note If *key is NULL, a apr_crypto_key_t will be created from a pool. If
* *key is not NULL, *key must point at a previously created structure.
* @param key The key returned, see note.
* @param ivSize The size of the initialisation vector will be returned, based
* on whether an IV is relevant for this type of crypto.
* @param pass The passphrase to use.
* @param passLen The passphrase length in bytes
* @param salt The salt to use.
* @param saltLen The salt length in bytes
* @param type 3DES_192, AES_128, AES_192, AES_256.
* @param mode Electronic Code Book / Cipher Block Chaining.
* @param doPad Pad if necessary.
* @param iterations Iteration count
* @param f The context to use.
* @param p The pool to use.
* @return Returns APR_ENOKEY if the pass phrase is missing or empty, or if a backend
* error occurred while generating the key. APR_ENOCIPHER if the type or mode
* is not supported by the particular backend. APR_EKEYTYPE if the key type is
* not known. APR_EPADDING if padding was requested but is not supported.
* APR_ENOTIMPL if not implemented.
*/
static apr_status_t crypto_passphrase(apr_crypto_key_t **k, apr_size_t *ivSize,
const char *pass, apr_size_t passLen, const unsigned char * salt,
apr_size_t saltLen, const apr_crypto_block_key_type_e type,
const apr_crypto_block_key_mode_e mode, const int doPad,
const int iterations, const apr_crypto_t *f, apr_pool_t *p)
{
apr_crypto_key_t *key = *k;
apr_crypto_key_rec_t *rec;
apr_status_t rv;
if (!key) {
*k = key = apr_pcalloc(p, sizeof *key);
if (!key) {
return APR_ENOMEM;
}
}
key->f = f;
key->provider = f->provider;
key->rec = rec = apr_pcalloc(p, sizeof(apr_crypto_key_rec_t));
if (!key->rec) {
return APR_ENOMEM;
}
rec->ktype = APR_CRYPTO_KTYPE_PASSPHRASE;
/* decide on what cipher mechanism we will be using */
rv = crypto_cipher_mechanism(key, type, mode, doPad, p);
if (APR_SUCCESS != rv) {
return rv;
}
/* generate the key */
if (PKCS5_PBKDF2_HMAC_SHA1(pass, passLen, (unsigned char *) salt, saltLen,
iterations, key->keyLen, key->key) == 0) {
return APR_ENOKEY;
}
key->doPad = doPad;
/* note: openssl incorrectly returns non zero IV size values for ECB
* algorithms, so work around this by ignoring the IV size.
*/
if (APR_MODE_ECB != mode) {
key->ivSize = EVP_CIPHER_iv_length(key->cipher);
}
if (ivSize) {
*ivSize = key->ivSize;
}
return APR_SUCCESS;
}
/**
* @brief Initialise a context for encrypting arbitrary data using the given key.
* @note If *ctx is NULL, a apr_crypto_block_t will be created from a pool. If
* *ctx is not NULL, *ctx must point at a previously created structure.
* @param ctx The block context returned, see note.
* @param iv Optional initialisation vector. If the buffer pointed to is NULL,
* an IV will be created at random, in space allocated from the pool.
* If the buffer pointed to is not NULL, the IV in the buffer will be
* used.
* @param key The key structure.
* @param blockSize The block size of the cipher.
* @param p The pool to use.
* @return Returns APR_ENOIV if an initialisation vector is required but not specified.
* Returns APR_EINIT if the backend failed to initialise the context. Returns
* APR_ENOTIMPL if not implemented.
*/
static apr_status_t crypto_block_encrypt_init(apr_crypto_block_t **ctx,
const unsigned char **iv, const apr_crypto_key_t *key,
apr_size_t *blockSize, apr_pool_t *p)
{
unsigned char *usedIv;
apr_crypto_config_t *config = key->f->config;
apr_crypto_block_t *block = *ctx;
if (!block) {
*ctx = block = apr_pcalloc(p, sizeof(apr_crypto_block_t));
}
if (!block) {
return APR_ENOMEM;
}
block->f = key->f;
block->pool = p;
block->provider = key->provider;
block->key = key;
apr_pool_cleanup_register(p, block, crypto_block_cleanup_helper,
apr_pool_cleanup_null);
switch (key->rec->ktype) {
case APR_CRYPTO_KTYPE_PASSPHRASE:
case APR_CRYPTO_KTYPE_SECRET: {
/* create a new context for encryption */
if (!block->initialised) {
block->cipherCtx = EVP_CIPHER_CTX_new();
block->initialised = 1;
}
/* generate an IV, if necessary */
usedIv = NULL;
if (key->ivSize) {
if (iv == NULL) {
return APR_ENOIV;
}
if (*iv == NULL) {
usedIv = apr_pcalloc(p, key->ivSize);
if (!usedIv) {
return APR_ENOMEM;
}
apr_crypto_clear(p, usedIv, key->ivSize);
if (!((RAND_status() == 1)
&& (RAND_bytes(usedIv, key->ivSize) == 1))) {
return APR_ENOIV;
}
*iv = usedIv;
}
else {
usedIv = (unsigned char *) *iv;
}
}
/* set up our encryption context */
#if CRYPTO_OPENSSL_CONST_BUFFERS
if (!EVP_EncryptInit_ex(block->cipherCtx, key->cipher, config->engine,
key->key, usedIv)) {
#else
if (!EVP_EncryptInit_ex(block->cipherCtx, key->cipher, config->engine, (unsigned char *) key->key, (unsigned char *) usedIv)) {
#endif
return APR_EINIT;
}
/* Clear up any read padding */
if (!EVP_CIPHER_CTX_set_padding(block->cipherCtx, key->doPad)) {
return APR_EPADDING;
}
if (blockSize) {
*blockSize = EVP_CIPHER_block_size(key->cipher);
}
return APR_SUCCESS;
}
default: {
return APR_EINVAL;
}
}
}
/**
* @brief Encrypt data provided by in, write it to out.
* @note The number of bytes written will be written to outlen. If
* out is NULL, outlen will contain the maximum size of the
* buffer needed to hold the data, including any data
* generated by apr_crypto_block_encrypt_finish below. If *out points
* to NULL, a buffer sufficiently large will be created from
* the pool provided. If *out points to a not-NULL value, this
* value will be used as a buffer instead.
* @param out Address of a buffer to which data will be written,
* see note.
* @param outlen Length of the output will be written here.
* @param in Address of the buffer to read.
* @param inlen Length of the buffer to read.
* @param ctx The block context to use.
* @return APR_ECRYPT if an error occurred. Returns APR_ENOTIMPL if
* not implemented.
*/
static apr_status_t crypto_block_encrypt(unsigned char **out,
apr_size_t *outlen, const unsigned char *in, apr_size_t inlen,
apr_crypto_block_t *block)
{
switch (block->key->rec->ktype) {
case APR_CRYPTO_KTYPE_PASSPHRASE:
case APR_CRYPTO_KTYPE_SECRET: {
int outl = *outlen;
unsigned char *buffer;
/* are we after the maximum size of the out buffer? */
if (!out) {
*outlen = inlen + EVP_MAX_BLOCK_LENGTH;
return APR_SUCCESS;
}
/* must we allocate the output buffer from a pool? */
if (!*out) {
buffer = apr_palloc(block->pool, inlen + EVP_MAX_BLOCK_LENGTH);
if (!buffer) {
return APR_ENOMEM;
}
apr_crypto_clear(block->pool, buffer, inlen + EVP_MAX_BLOCK_LENGTH);
*out = buffer;
}
#if CRYPT_OPENSSL_CONST_BUFFERS
if (!EVP_EncryptUpdate(block->cipherCtx, (*out), &outl, in, inlen)) {
#else
if (!EVP_EncryptUpdate(block->cipherCtx, (*out), &outl,
(unsigned char *) in, inlen)) {
#endif
crypto_block_cleanup(block);
return APR_ECRYPT;
}
*outlen = outl;
return APR_SUCCESS;
}
default: {
return APR_EINVAL;
}
}
}
/**
* @brief Encrypt final data block, write it to out.
* @note If necessary the final block will be written out after being
* padded. Typically the final block will be written to the
* same buffer used by apr_crypto_block_encrypt, offset by the
* number of bytes returned as actually written by the
* apr_crypto_block_encrypt() call. After this call, the context
* is cleaned and can be reused by apr_crypto_block_encrypt_init().
* @param out Address of a buffer to which data will be written. This
* buffer must already exist, and is usually the same
* buffer used by apr_evp_crypt(). See note.
* @param outlen Length of the output will be written here.
* @param ctx The block context to use.
* @return APR_ECRYPT if an error occurred.
* @return APR_EPADDING if padding was enabled and the block was incorrectly
* formatted.
* @return APR_ENOTIMPL if not implemented.
*/
static apr_status_t crypto_block_encrypt_finish(unsigned char *out,
apr_size_t *outlen, apr_crypto_block_t *block)
{
switch (block->key->rec->ktype) {
case APR_CRYPTO_KTYPE_PASSPHRASE:
case APR_CRYPTO_KTYPE_SECRET: {
apr_status_t rc = APR_SUCCESS;
int len = *outlen;
if (EVP_EncryptFinal_ex(block->cipherCtx, out, &len) == 0) {
rc = APR_EPADDING;
}
else {
*outlen = len;
}
crypto_block_cleanup(block);
return rc;
}
default: {
return APR_EINVAL;
}
}
}
/**
* @brief Initialise a context for decrypting arbitrary data using the given key.
* @note If *ctx is NULL, a apr_crypto_block_t will be created from a pool. If
* *ctx is not NULL, *ctx must point at a previously created structure.
* @param ctx The block context returned, see note.
* @param blockSize The block size of the cipher.
* @param iv Optional initialisation vector. If the buffer pointed to is NULL,
* an IV will be created at random, in space allocated from the pool.
* If the buffer is not NULL, the IV in the buffer will be used.
* @param key The key structure.
* @param p The pool to use.
* @return Returns APR_ENOIV if an initialisation vector is required but not specified.
* Returns APR_EINIT if the backend failed to initialise the context. Returns
* APR_ENOTIMPL if not implemented.
*/
static apr_status_t crypto_block_decrypt_init(apr_crypto_block_t **ctx,
apr_size_t *blockSize, const unsigned char *iv,
const apr_crypto_key_t *key, apr_pool_t *p)
{
apr_crypto_config_t *config = key->f->config;
apr_crypto_block_t *block = *ctx;
if (!block) {
*ctx = block = apr_pcalloc(p, sizeof(apr_crypto_block_t));
}
if (!block) {
return APR_ENOMEM;
}
block->f = key->f;
block->pool = p;
block->provider = key->provider;
block->key = key;
apr_pool_cleanup_register(p, block, crypto_block_cleanup_helper,
apr_pool_cleanup_null);
switch (key->rec->ktype) {
case APR_CRYPTO_KTYPE_PASSPHRASE:
case APR_CRYPTO_KTYPE_SECRET: {
/* create a new context for encryption */
if (!block->initialised) {
block->cipherCtx = EVP_CIPHER_CTX_new();
block->initialised = 1;
}
/* generate an IV, if necessary */
if (key->ivSize) {
if (iv == NULL) {
return APR_ENOIV;
}
}
/* set up our encryption context */
#if CRYPTO_OPENSSL_CONST_BUFFERS
if (!EVP_DecryptInit_ex(block->cipherCtx, key->cipher, config->engine,
key->key, iv)) {
#else
if (!EVP_DecryptInit_ex(block->cipherCtx, key->cipher, config->engine, (unsigned char *) key->key, (unsigned char *) iv)) {
#endif
return APR_EINIT;
}
/* Clear up any read padding */
if (!EVP_CIPHER_CTX_set_padding(block->cipherCtx, key->doPad)) {
return APR_EPADDING;
}
if (blockSize) {
*blockSize = EVP_CIPHER_block_size(key->cipher);
}
return APR_SUCCESS;
}
default: {
return APR_EINVAL;
}
}
}
/**
* @brief Decrypt data provided by in, write it to out.
* @note The number of bytes written will be written to outlen. If
* out is NULL, outlen will contain the maximum size of the
* buffer needed to hold the data, including any data
* generated by apr_crypto_block_decrypt_finish below. If *out points
* to NULL, a buffer sufficiently large will be created from
* the pool provided. If *out points to a not-NULL value, this
* value will be used as a buffer instead.
* @param out Address of a buffer to which data will be written,
* see note.
* @param outlen Length of the output will be written here.
* @param in Address of the buffer to read.
* @param inlen Length of the buffer to read.
* @param ctx The block context to use.
* @return APR_ECRYPT if an error occurred. Returns APR_ENOTIMPL if
* not implemented.
*/
static apr_status_t crypto_block_decrypt(unsigned char **out,
apr_size_t *outlen, const unsigned char *in, apr_size_t inlen,
apr_crypto_block_t *block)
{
switch (block->key->rec->ktype) {
case APR_CRYPTO_KTYPE_PASSPHRASE:
case APR_CRYPTO_KTYPE_SECRET: {
int outl = *outlen;
unsigned char *buffer;
/* are we after the maximum size of the out buffer? */
if (!out) {
*outlen = inlen + EVP_MAX_BLOCK_LENGTH;
return APR_SUCCESS;
}
/* must we allocate the output buffer from a pool? */
if (!(*out)) {
buffer = apr_palloc(block->pool, inlen + EVP_MAX_BLOCK_LENGTH);
if (!buffer) {
return APR_ENOMEM;
}
apr_crypto_clear(block->pool, buffer, inlen + EVP_MAX_BLOCK_LENGTH);
*out = buffer;
}
#if CRYPT_OPENSSL_CONST_BUFFERS
if (!EVP_DecryptUpdate(block->cipherCtx, *out, &outl, in, inlen)) {
#else
if (!EVP_DecryptUpdate(block->cipherCtx, *out, &outl, (unsigned char *) in,
inlen)) {
#endif
crypto_block_cleanup(block);
return APR_ECRYPT;
}
*outlen = outl;
return APR_SUCCESS;
}
default: {
return APR_EINVAL;
}
}
}
/**
* @brief Decrypt final data block, write it to out.
* @note If necessary the final block will be written out after being
* padded. Typically the final block will be written to the
* same buffer used by apr_crypto_block_decrypt, offset by the
* number of bytes returned as actually written by the
* apr_crypto_block_decrypt() call. After this call, the context
* is cleaned and can be reused by apr_crypto_block_decrypt_init().
* @param out Address of a buffer to which data will be written. This
* buffer must already exist, and is usually the same
* buffer used by apr_evp_crypt(). See note.
* @param outlen Length of the output will be written here.
* @param ctx The block context to use.
* @return APR_ECRYPT if an error occurred.
* @return APR_EPADDING if padding was enabled and the block was incorrectly
* formatted.
* @return APR_ENOTIMPL if not implemented.
*/
static apr_status_t crypto_block_decrypt_finish(unsigned char *out,
apr_size_t *outlen, apr_crypto_block_t *block)
{
switch (block->key->rec->ktype) {
case APR_CRYPTO_KTYPE_PASSPHRASE:
case APR_CRYPTO_KTYPE_SECRET: {
apr_status_t rc = APR_SUCCESS;
int len = *outlen;
if (EVP_DecryptFinal_ex(block->cipherCtx, out, &len) == 0) {
rc = APR_EPADDING;
}
else {
*outlen = len;
}
crypto_block_cleanup(block);
return rc;
}
default: {
return APR_EINVAL;
}
}
}
static apr_status_t crypto_digest_init(apr_crypto_digest_t **d,
const apr_crypto_key_t *key, apr_crypto_digest_rec_t *rec, apr_pool_t *p)
{
apr_crypto_config_t *config = key->f->config;
apr_crypto_digest_t *digest = *d;
if (!digest) {
*d = digest = apr_pcalloc(p, sizeof(apr_crypto_digest_t));
}
if (!digest) {
return APR_ENOMEM;
}
digest->f = key->f;
digest->pool = p;
digest->provider = key->provider;
digest->key = key;
digest->rec = rec;
/* create a new context for digest */
if (!digest->initialised) {
digest->mdCtx = EVP_MD_CTX_new();
digest->initialised = 1;
}
apr_pool_cleanup_register(p, digest, crypto_digest_cleanup_helper,
apr_pool_cleanup_null);
switch (key->rec->ktype) {
case APR_CRYPTO_KTYPE_HASH: {
if (1
!= EVP_DigestInit_ex(digest->mdCtx, key->hmac,
config->engine)) {
return APR_EINIT;
}
break;
}
case APR_CRYPTO_KTYPE_HMAC:
case APR_CRYPTO_KTYPE_CMAC: {
if (1
!= EVP_DigestSignInit(digest->mdCtx, NULL, key->hmac,
config->engine, key->pkey)) {
return APR_EINIT;
}
break;
}
default: {
return APR_EINVAL;
}
}
return APR_SUCCESS;
}
static apr_status_t crypto_digest_update(apr_crypto_digest_t *digest,
const unsigned char *in, apr_size_t inlen)
{
switch (digest->key->rec->ktype) {
case APR_CRYPTO_KTYPE_HASH: {
if (1 != EVP_DigestUpdate(digest->mdCtx, in, inlen)) {
crypto_digest_cleanup(digest);
return APR_ECRYPT;
}
return APR_SUCCESS;
}
case APR_CRYPTO_KTYPE_HMAC:
case APR_CRYPTO_KTYPE_CMAC: {
if (1 != EVP_DigestSignUpdate(digest->mdCtx, in, inlen)) {
crypto_digest_cleanup(digest);
return APR_ECRYPT;
}
return APR_SUCCESS;
}
default: {
return APR_EINVAL;
}
}
}
static apr_status_t crypto_digest_final(apr_crypto_digest_t *digest)
{
switch (digest->key->rec->ktype) {
case APR_CRYPTO_KTYPE_HASH: {
apr_status_t status = APR_SUCCESS;
unsigned int len = EVP_MD_CTX_size(digest->mdCtx);
switch (digest->rec->dtype) {
case APR_CRYPTO_DTYPE_HASH: {
/* must we allocate the output buffer from a pool? */
if (!digest->rec->d.hash.s || digest->rec->d.hash.slen != len) {
digest->rec->d.hash.slen = len;
digest->rec->d.hash.s = apr_palloc(digest->pool, len);
if (!digest->rec->d.hash.s) {
return APR_ENOMEM;
}
apr_crypto_clear(digest->pool, digest->rec->d.hash.s, len);
}
/* then, determine the signature */
if (EVP_DigestFinal_ex(digest->mdCtx, digest->rec->d.hash.s, &len)
== 0) {
status = APR_ECRYPT;
}
break;
}
default:
status = APR_ENODIGEST;
}
crypto_digest_cleanup(digest);
return status;
}
case APR_CRYPTO_KTYPE_HMAC:
case APR_CRYPTO_KTYPE_CMAC: {
apr_status_t status = APR_SUCCESS;
size_t len;
/* first, determine the signature length */
if (1 != EVP_DigestSignFinal(digest->mdCtx, NULL, &len)) {
status = APR_ECRYPT;
} else {
switch (digest->rec->dtype) {
case APR_CRYPTO_DTYPE_SIGN: {
/* must we allocate the output buffer from a pool? */
if (!digest->rec->d.sign.s || digest->rec->d.sign.slen != len) {
digest->rec->d.sign.slen = len;
digest->rec->d.sign.s = apr_palloc(digest->pool, len);
if (!digest->rec->d.sign.s) {
return APR_ENOMEM;
}
apr_crypto_clear(digest->pool, digest->rec->d.sign.s, len);
}
/* then, determine the signature */
if (EVP_DigestSignFinal(digest->mdCtx, digest->rec->d.sign.s,
&len) == 0) {
status = APR_ECRYPT;
}
break;
}
case APR_CRYPTO_DTYPE_VERIFY: {
/* must we allocate the output buffer from a pool? */
if (!digest->rec->d.verify.s
|| digest->rec->d.verify.slen != len) {
digest->rec->d.verify.slen = len;
digest->rec->d.verify.s = apr_palloc(digest->pool, len);
if (!digest->rec->d.verify.s) {
return APR_ENOMEM;
}
apr_crypto_clear(digest->pool, digest->rec->d.verify.s,
len);
}
/* then, determine the signature */
if (EVP_DigestSignFinal(digest->mdCtx, digest->rec->d.verify.s,
&len) == 0) {
status = APR_ECRYPT;
} else if (digest->rec->d.verify.slen
== digest->rec->d.verify.vlen) {
status =
CRYPTO_memcmp(digest->rec->d.verify.s,
digest->rec->d.verify.v,
digest->rec->d.verify.slen) ?
APR_ENOVERIFY : APR_SUCCESS;
} else {
status = APR_ENOVERIFY;
}
break;
}
default:
status = APR_ENODIGEST;
}
}
crypto_digest_cleanup(digest);
return status;
}
default: {
return APR_EINVAL;
}
}
}
static apr_status_t crypto_digest(
const apr_crypto_key_t *key, apr_crypto_digest_rec_t *rec, const unsigned char *in,
apr_size_t inlen, apr_pool_t *p)
{
apr_crypto_digest_t *digest = NULL;
apr_status_t status = APR_SUCCESS;
status = crypto_digest_init(&digest, key, rec, p);
if (APR_SUCCESS == status) {
status = crypto_digest_update(digest, in, inlen);
if (APR_SUCCESS == status) {
status = crypto_digest_final(digest);
}
}
return status;
}
static void cprng_stream_ctx_free(cprng_stream_ctx_t *sctx)
{
if (sctx->ctx) {
EVP_CIPHER_CTX_free(sctx->ctx);
}
if (sctx->malloced) {
free(sctx);
}
}
static apr_status_t cprng_stream_ctx_make(cprng_stream_ctx_t **psctx,
apr_crypto_t *f, apr_crypto_cipher_e cipher, apr_pool_t *pool)
{
cprng_stream_ctx_t *sctx;
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *ecipher;
*psctx = NULL;
if (pool) {
sctx = apr_palloc(pool, sizeof(cprng_stream_ctx_t));
}
else {
sctx = malloc(sizeof(cprng_stream_ctx_t));
}
if (!sctx) {
return APR_ENOMEM;
}
sctx->malloced = !pool;
sctx->ctx = ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
cprng_stream_ctx_free(sctx);
return APR_ENOMEM;
}
/* We only handle Chacha20 and AES256-CTR stream ciphers, for now.
* AES256-CTR should be in any openssl version of this century but is used
* only if Chacha20 is missing (openssl < 1.1). This is because Chacha20 is
* fast (enough) in software and timing attacks safe, though AES256-CTR can
* be faster and constant-time but only when the CPU (aesni) or some crypto
* hardware are in place.
*/
switch (cipher) {
case APR_CRYPTO_CIPHER_AUTO: {
#if defined(NID_chacha20)
ecipher = EVP_chacha20();
#elif defined(NID_aes_256_ctr)
ecipher = EVP_aes_256_ctr();
#else
cprng_stream_ctx_free(sctx);
return APR_ENOCIPHER;
#endif
}
case APR_CRYPTO_CIPHER_AES_256_CTR: {
#if defined(NID_aes_256_ctr)
ecipher = EVP_aes_256_ctr();
break;
#else
cprng_stream_ctx_free(sctx);
return APR_ENOCIPHER;
#endif
}
case APR_CRYPTO_CIPHER_CHACHA20: {
#if defined(NID_chacha20)
ecipher = EVP_chacha20();
break;
#else
cprng_stream_ctx_free(sctx);
return APR_ENOCIPHER;
#endif
}
default: {
cprng_stream_ctx_free(sctx);
return APR_ENOCIPHER;
}
}
if (EVP_EncryptInit_ex(ctx, ecipher, f->config->engine, NULL, NULL) <= 0) {
cprng_stream_ctx_free(sctx);
return APR_ENOMEM;
}
*psctx = sctx;
return APR_SUCCESS;
}
static APR_INLINE
void cprng_stream_setkey(cprng_stream_ctx_t *sctx,
const unsigned char *key,
const unsigned char *iv)
{
switch(EVP_CIPHER_CTX_nid(sctx->ctx)) {
#if defined(NID_chacha20)
case NID_chacha20:
/* With CHACHA20, iv=NULL is the same as zeros but it's faster
* to (re-)init; use that for efficiency.
*/
EVP_EncryptInit_ex(sctx->ctx, NULL, NULL, key, NULL);
break;
#endif
#if defined(NID_aes_256_ctr)
case NID_aes_256_ctr:
/* With AES256-CTR, iv=NULL seems to peek up and random one (for
* the initial CTR), while we can live with zeros (fixed CTR);
* efficiency still.
*/
EVP_EncryptInit_ex(sctx->ctx, NULL, NULL, key, iv);
break;
#endif
default:
assert(0);
break;
}
}
static apr_status_t cprng_stream_ctx_bytes(cprng_stream_ctx_t **pctx,
unsigned char *key, unsigned char *to, apr_size_t n, const unsigned char *z)
{
cprng_stream_ctx_t *sctx = *pctx;
int len;
/* We never encrypt twice with the same key, so no IV is needed (can
* be zeros). When EVP_EncryptInit() is called multiple times it clears
* its previous resources appropriately, and since we don't want the key
* and its keystream to reside in memory at the same time, we have to
* EVP_EncryptInit() twice: firstly to set the key and then finally to
* overwrite the key (with zeros) after the keystream is produced.
* As for EVP_EncryptFinish(), we don't need it either because padding
* is disabled (irrelevant for a stream cipher).
*/
cprng_stream_setkey(sctx, key, z);
EVP_CIPHER_CTX_set_padding(sctx->ctx, 0);
EVP_EncryptUpdate(sctx->ctx, key, &len, z, CPRNG_KEY_SIZE);
if (n) {
EVP_EncryptUpdate(sctx->ctx, to, &len, z, n);
}
cprng_stream_setkey(sctx, z, z);
return APR_SUCCESS;
}
/**
* OpenSSL module.
*/
APR_MODULE_DECLARE_DATA const apr_crypto_driver_t apr_crypto_openssl_driver = {
"openssl", crypto_init, crypto_make, crypto_get_block_key_digests, crypto_get_block_key_types,
crypto_get_block_key_modes, crypto_passphrase,
crypto_block_encrypt_init, crypto_block_encrypt,
crypto_block_encrypt_finish, crypto_block_decrypt_init,
crypto_block_decrypt, crypto_block_decrypt_finish,
crypto_digest_init, crypto_digest_update, crypto_digest_final, crypto_digest,
crypto_block_cleanup, crypto_digest_cleanup, crypto_cleanup, crypto_shutdown, crypto_error,
crypto_key, cprng_stream_ctx_make, cprng_stream_ctx_free, cprng_stream_ctx_bytes
};
#endif