From c0e5d956315bad00901e98e3334ebed62159c4e5 Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Sun, 22 Jul 2018 13:11:32 +0000 Subject: [PATCH] apr_crypto: Add support for digest functions, with hashing, signing and verifying. git-svn-id: https://svn.apache.org/repos/asf/apr/apr/trunk@1836439 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 3 + crypto/apr_crypto.c | 74 ++ crypto/apr_crypto_commoncrypto.c | 969 +++++++++++++++++++----- crypto/apr_crypto_nss.c | 1005 ++++++++++++++++++++----- crypto/apr_crypto_openssl.c | 904 ++++++++++++++++++---- include/apr_crypto.h | 556 +++++++++++++- include/apu_errno.h | 7 + include/private/apr_crypto_internal.h | 77 ++ test/testcrypto.c | 948 ++++++++++++++++++++++- 9 files changed, 3981 insertions(+), 562 deletions(-) diff --git a/CHANGES b/CHANGES index d510cb342a9..b201082c7bc 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes for APR 2.0.0 + *) apr_crypto: Add support for digest functions, with hashing, signing + and verifying. [Graham Leggett] + *) apr_json: Add support for encoding and decoding RFC8259 JSON. [Moriyoshi Koizumi ] diff --git a/crypto/apr_crypto.c b/crypto/apr_crypto.c index bda84b2a267..24e36f30adb 100644 --- a/crypto/apr_crypto.c +++ b/crypto/apr_crypto.c @@ -54,6 +54,12 @@ APR_TYPEDEF_STRUCT(apr_crypto_block_t, const apr_crypto_t *f; ) +APR_TYPEDEF_STRUCT(apr_crypto_digest_t, + apr_pool_t *pool; + apr_crypto_driver_t *provider; + const apr_crypto_t *f; +) + typedef struct apr_crypto_clear_t { void *buffer; apr_size_t size; @@ -199,6 +205,24 @@ APR_DECLARE(int) apr_crypto_equals(const void *buf1, const void *buf2, return 1 & ((diff - 1) >> 8); } +APR_DECLARE(apr_crypto_key_rec_t *) apr_crypto_key_rec_make( + apr_crypto_key_type ktype, apr_pool_t *p) +{ + apr_crypto_key_rec_t *key = apr_pcalloc(p, sizeof(apr_crypto_key_rec_t)); + key->ktype = ktype; + return key; +} + +APR_DECLARE(apr_crypto_digest_rec_t *) apr_crypto_digest_rec_make( + apr_crypto_digest_type_e dtype, apr_pool_t *p) +{ + apr_crypto_digest_rec_t *rec = apr_pcalloc(p, sizeof(apr_crypto_digest_rec_t)); + if (rec) { + rec->dtype = dtype; + } + return rec; +} + APR_DECLARE(apr_status_t) apr_crypto_get_driver( const apr_crypto_driver_t **driver, const char *name, const char *params, const apu_err_t **result, apr_pool_t *pool) @@ -644,6 +668,21 @@ APR_DECLARE(apr_status_t) apr_crypto_make(apr_crypto_t **f, return driver->make(f, driver, params, pool); } +/** + * @brief Get a hash table of digests, keyed by the name of the digest against + * a pointer to apr_crypto_digest_t, which in turn begins with an + * integer. + * + * @param digests - hashtable of digests keyed to constants. + * @param f - encryption context + * @return APR_SUCCESS for success + */ +APR_DECLARE(apr_status_t) apr_crypto_get_block_key_digests(apr_hash_t **digests, + const apr_crypto_t *f) +{ + return f->provider->get_block_key_digests(digests, f); +} + /** * @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, which in turn begins with an @@ -876,6 +915,30 @@ APR_DECLARE(apr_status_t) apr_crypto_block_decrypt_finish(unsigned char *out, return ctx->provider->block_decrypt_finish(out, outlen, ctx); } +APR_DECLARE(apr_status_t) apr_crypto_digest_init(apr_crypto_digest_t **d, + const apr_crypto_key_t *key, apr_crypto_digest_rec_t *rec, apr_pool_t *p) +{ + return key->provider->digest_init(d, key, rec, p); +} + +APR_DECLARE(apr_status_t) apr_crypto_digest_update(apr_crypto_digest_t *digest, + const unsigned char *in, apr_size_t inlen) +{ + return digest->provider->digest_update(digest, in, inlen); +} + +APR_DECLARE(apr_status_t) apr_crypto_digest_final(apr_crypto_digest_t *digest) +{ + return digest->provider->digest_final(digest); +} + +APR_DECLARE(apr_status_t) apr_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) +{ + return key->provider->digest(key, rec, in, inlen, p); +} + /** * @brief Clean encryption / decryption context. * @note After cleanup, a context is free to be reused if necessary. @@ -887,6 +950,17 @@ APR_DECLARE(apr_status_t) apr_crypto_block_cleanup(apr_crypto_block_t *ctx) return ctx->provider->block_cleanup(ctx); } +/** + * @brief Clean sign / verify context. + * @note After cleanup, a context is free to be reused if necessary. + * @param ctx The digest context to use. + * @return Returns APR_ENOTIMPL if not supported. + */ +APR_DECLARE(apr_status_t) apr_crypto_digest_cleanup(apr_crypto_digest_t *ctx) +{ + return ctx->provider->digest_cleanup(ctx); +} + /** * @brief Clean encryption / decryption context. * @note After cleanup, a context is free to be reused if necessary. diff --git a/crypto/apr_crypto_commoncrypto.c b/crypto/apr_crypto_commoncrypto.c index 83886bce3af..f2095bcd7ad 100644 --- a/crypto/apr_crypto_commoncrypto.c +++ b/crypto/apr_crypto_commoncrypto.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "apr.h" #include "apr_lib.h" #include "apu.h" #include "apr_private.h" @@ -33,6 +34,7 @@ #if APU_HAVE_CRYPTO #include +#include #define LOG_PREFIX "apr_crypto_commoncrypto: " @@ -41,6 +43,7 @@ struct apr_crypto_t apr_pool_t *pool; const apr_crypto_driver_t *provider; apu_err_t *result; + apr_hash_t *digests; apr_hash_t *types; apr_hash_t *modes; apr_random_t *rng; @@ -51,12 +54,16 @@ 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; + unsigned char *key; + void *hash; CCAlgorithm algorithm; CCOptions options; - unsigned char *key; int keyLen; int ivSize; + CCHmacAlgorithm hmac; apr_size_t blockSize; + apr_size_t digestSize; }; struct apr_crypto_block_t @@ -68,6 +75,27 @@ struct apr_crypto_block_t CCCryptorRef ref; }; +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; + CCHmacContext *hmac; + void *hash; + unsigned char *md; +}; + +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 }, @@ -95,16 +123,25 @@ static apr_status_t crypto_error(const apu_err_t **result, */ static apr_status_t crypto_shutdown(void) { - return apr_crypto_lib_term("commoncrypto"); + 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) + const apu_err_t **result) { - return apr_crypto_lib_init("commoncrypto", params, result, pool); + + apr_pool_cleanup_register(pool, pool, crypto_shutdown_helper, + apr_pool_cleanup_null); + + return APR_SUCCESS; } /** @@ -131,6 +168,25 @@ static apr_status_t crypto_block_cleanup_helper(void *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 digest context to use. + * @return Returns APR_ENOTIMPL if not supported. + */ +static apr_status_t crypto_digest_cleanup(apr_crypto_digest_t *ctx) +{ + + 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. @@ -168,6 +224,7 @@ static apr_status_t crypto_make(apr_crypto_t **ff, { apr_crypto_t *f = apr_pcalloc(pool, sizeof(apr_crypto_t)); apr_status_t rv; + int i; if (!f) { return APR_ENOMEM; @@ -196,21 +253,34 @@ static apr_status_t crypto_make(apr_crypto_t **ff, return APR_ENOMEM; } + f->digests = apr_hash_make(pool); + if (!f->digests) { + return APR_ENOMEM; + } + apr_hash_set(f->digests, "md2", APR_HASH_KEY_STRING, &(key_digests[i = 0])); + apr_hash_set(f->digests, "md4", APR_HASH_KEY_STRING, &(key_digests[++i])); + apr_hash_set(f->digests, "md5", APR_HASH_KEY_STRING, &(key_digests[++i])); + 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[0])); - apr_hash_set(f->types, "aes128", APR_HASH_KEY_STRING, &(key_types[1])); - apr_hash_set(f->types, "aes192", APR_HASH_KEY_STRING, &(key_types[2])); - apr_hash_set(f->types, "aes256", APR_HASH_KEY_STRING, &(key_types[3])); + 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[0])); - apr_hash_set(f->modes, "cbc", APR_HASH_KEY_STRING, &(key_modes[1])); + 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])); apr_pool_cleanup_register(pool, f, crypto_cleanup_helper, apr_pool_cleanup_null); @@ -219,6 +289,21 @@ static apr_status_t crypto_make(apr_crypto_t **ff, } +/** + * @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. @@ -350,6 +435,36 @@ static apr_status_t crypto_cipher_mechanism(apr_crypto_key_t *key, return APR_SUCCESS; } +static apr_status_t crypto_digest_mechanism(apr_crypto_key_t *key, + const apr_crypto_block_key_digest_e digest, apr_pool_t *p) +{ + /* determine the digest algorithm to be used */ + switch (digest) { + case APR_CRYPTO_DIGEST_MD5: + key->digestSize = CC_MD5_DIGEST_LENGTH; + break; + case APR_CRYPTO_DIGEST_SHA1: + key->digestSize = CC_SHA1_DIGEST_LENGTH; + break; + case APR_CRYPTO_DIGEST_SHA224: + key->digestSize = CC_SHA224_DIGEST_LENGTH; + break; + case APR_CRYPTO_DIGEST_SHA256: + key->digestSize = CC_SHA256_DIGEST_LENGTH; + break; + case APR_CRYPTO_DIGEST_SHA384: + key->digestSize = CC_SHA384_DIGEST_LENGTH; + break; + case APR_CRYPTO_DIGEST_SHA512: + key->digestSize = CC_SHA512_DIGEST_LENGTH; + break; + default: + return APR_ENODIGEST; + } + + 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 @@ -379,19 +494,21 @@ static apr_status_t crypto_key(apr_crypto_key_t **k, return APR_ENOMEM; } + key->pool = p; key->f = f; key->provider = f->provider; - - /* 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; - } + 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 ((f->result->rc = CCKeyDerivationPBKDF(kCCPBKDF2, rec->k.passphrase.pass, rec->k.passphrase.passLen, @@ -406,6 +523,12 @@ static apr_status_t crypto_key(apr_crypto_key_t **k, 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; @@ -417,6 +540,87 @@ static apr_status_t crypto_key(apr_crypto_key_t **k, break; } + case APR_CRYPTO_KTYPE_HASH: { + + /* decide on what digest mechanism we will be using */ + rv = crypto_digest_mechanism(key, rec->k.hash.digest, p); + if (APR_SUCCESS != rv) { + return rv; + } + + switch (rec->k.hash.digest) { + case APR_CRYPTO_DIGEST_MD5: + key->digestSize = CC_MD5_DIGEST_LENGTH; + break; + case APR_CRYPTO_DIGEST_SHA1: + key->digestSize = CC_SHA1_DIGEST_LENGTH; + break; + case APR_CRYPTO_DIGEST_SHA224: + key->digestSize = CC_SHA224_DIGEST_LENGTH; + break; + case APR_CRYPTO_DIGEST_SHA256: + key->digestSize = CC_SHA256_DIGEST_LENGTH; + break; + case APR_CRYPTO_DIGEST_SHA384: + key->digestSize = CC_SHA384_DIGEST_LENGTH; + break; + case APR_CRYPTO_DIGEST_SHA512: + key->digestSize = CC_SHA512_DIGEST_LENGTH; + break; + default: + return APR_ENODIGEST; + } + + break; + } + case APR_CRYPTO_KTYPE_HMAC: { + + /* 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; + } + + /* decide on what digest mechanism we will be using */ + rv = crypto_digest_mechanism(key, rec->k.hmac.digest, p); + if (APR_SUCCESS != rv) { + return rv; + } + + key->hmac = rec->k.hmac.digest; + + switch (rec->k.hmac.digest) { + case APR_CRYPTO_DIGEST_MD5: + key->hmac = kCCHmacAlgMD5; + break; + case APR_CRYPTO_DIGEST_SHA1: + key->hmac = kCCHmacAlgSHA1; + break; + case APR_CRYPTO_DIGEST_SHA224: + key->hmac = kCCHmacAlgSHA224; + break; + case APR_CRYPTO_DIGEST_SHA256: + key->hmac = kCCHmacAlgSHA256; + break; + case APR_CRYPTO_DIGEST_SHA384: + key->hmac = kCCHmacAlgSHA384; + break; + case APR_CRYPTO_DIGEST_SHA512: + key->hmac = kCCHmacAlgSHA512; + break; + default: + return APR_ENODIGEST; + } + + break; + } + + case APR_CRYPTO_KTYPE_CMAC: { + + return APR_ENOTIMPL; + + } + default: { return APR_ENOKEY; @@ -463,6 +667,7 @@ static apr_status_t crypto_passphrase(apr_crypto_key_t **k, apr_size_t *ivSize, { apr_status_t rv; apr_crypto_key_t *key = *k; + apr_crypto_key_rec_t *rec; if (!key) { *k = key = apr_pcalloc(p, sizeof *key); @@ -473,6 +678,11 @@ static apr_status_t crypto_passphrase(apr_crypto_key_t **k, apr_size_t *ivSize, 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); @@ -530,59 +740,72 @@ static apr_status_t crypto_block_encrypt_init(apr_crypto_block_t **ctx, apr_pool_cleanup_register(p, block, crypto_block_cleanup_helper, apr_pool_cleanup_null); - /* generate an IV, if necessary */ - usedIv = NULL; - if (key->ivSize) { - if (iv == NULL) { - return APR_ENOIV; - } - if (*iv == NULL) { - apr_status_t status; - usedIv = apr_pcalloc(p, key->ivSize); - if (!usedIv) { - return APR_ENOMEM; + switch (key->rec->ktype) { + + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { + + /* generate an IV, if necessary */ + usedIv = NULL; + if (key->ivSize) { + if (iv == NULL) { + return APR_ENOIV; } - apr_crypto_clear(p, usedIv, key->ivSize); - status = apr_random_secure_bytes(block->f->rng, usedIv, - key->ivSize); - if (APR_SUCCESS != status) { - return status; + if (*iv == NULL) { + apr_status_t status; + usedIv = apr_pcalloc(p, key->ivSize); + if (!usedIv) { + return APR_ENOMEM; + } + apr_crypto_clear(p, usedIv, key->ivSize); + status = apr_random_secure_bytes(block->f->rng, usedIv, + key->ivSize); + if (APR_SUCCESS != status) { + return status; + } + *iv = usedIv; + } else { + usedIv = (unsigned char *) *iv; } - *iv = usedIv; } - else { - usedIv = (unsigned char *) *iv; + + /* create a new context for encryption */ + switch ((block->f->result->rc = CCCryptorCreate(kCCEncrypt, + key->algorithm, key->options, key->key, key->keyLen, usedIv, + &block->ref))) { + case kCCSuccess: { + break; + } + case kCCParamError: { + return APR_EINIT; + } + case kCCMemoryFailure: { + return APR_ENOMEM; + } + case kCCAlignmentError: { + return APR_EPADDING; + } + case kCCUnimplemented: { + return APR_ENOTIMPL; + } + default: { + return APR_EINIT; + } } - } - /* create a new context for encryption */ - switch ((block->f->result->rc = CCCryptorCreate(kCCEncrypt, key->algorithm, - key->options, key->key, key->keyLen, usedIv, &block->ref))) { - case kCCSuccess: { - break; - } - case kCCParamError: { - return APR_EINIT; - } - case kCCMemoryFailure: { - return APR_ENOMEM; - } - case kCCAlignmentError: { - return APR_EPADDING; - } - case kCCUnimplemented: { - return APR_ENOTIMPL; + if (blockSize) { + *blockSize = key->blockSize; + } + + return APR_SUCCESS; + } default: { - return APR_EINIT; - } - } - if (blockSize) { - *blockSize = key->blockSize; - } + return APR_EINVAL; - return APR_SUCCESS; + } + } } @@ -606,43 +829,56 @@ static apr_status_t crypto_block_encrypt_init(apr_crypto_block_t **ctx, */ 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 *ctx) + apr_crypto_block_t *block) { - apr_size_t outl = *outlen; - unsigned char *buffer; + switch (block->key->rec->ktype) { - /* are we after the maximum size of the out buffer? */ - if (!out) { - *outlen = CCCryptorGetOutputLength(ctx->ref, inlen, 1); - return APR_SUCCESS; - } + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { - /* must we allocate the output buffer from a pool? */ - if (!*out) { - outl = CCCryptorGetOutputLength(ctx->ref, inlen, 1); - buffer = apr_palloc(ctx->pool, outl); - if (!buffer) { - return APR_ENOMEM; + apr_size_t outl = *outlen; + unsigned char *buffer; + + /* are we after the maximum size of the out buffer? */ + if (!out) { + *outlen = CCCryptorGetOutputLength(block->ref, inlen, 1); + return APR_SUCCESS; } - apr_crypto_clear(ctx->pool, buffer, outl); - *out = buffer; - } - switch ((ctx->f->result->rc = CCCryptorUpdate(ctx->ref, in, inlen, (*out), - outl, &outl))) { - case kCCSuccess: { - break; - } - case kCCBufferTooSmall: { - return APR_ENOSPACE; + /* must we allocate the output buffer from a pool? */ + if (!*out) { + outl = CCCryptorGetOutputLength(block->ref, inlen, 1); + buffer = apr_palloc(block->pool, outl); + if (!buffer) { + return APR_ENOMEM; + } + apr_crypto_clear(block->pool, buffer, outl); + *out = buffer; + } + + switch ((block->f->result->rc = CCCryptorUpdate(block->ref, in, inlen, (*out), + outl, &outl))) { + case kCCSuccess: { + break; + } + case kCCBufferTooSmall: { + return APR_ENOSPACE; + } + default: { + return APR_ECRYPT; + } + } + *outlen = outl; + + return APR_SUCCESS; + } default: { - return APR_ECRYPT; + + return APR_EINVAL; + } } - *outlen = outl; - - return APR_SUCCESS; } @@ -665,36 +901,49 @@ static apr_status_t crypto_block_encrypt(unsigned char **out, * @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 *ctx) + apr_size_t *outlen, apr_crypto_block_t *block) { - apr_size_t len = *outlen; + switch (block->key->rec->ktype) { - ctx->f->result->rc = CCCryptorFinal(ctx->ref, out, - CCCryptorGetOutputLength(ctx->ref, 0, 1), &len); + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { - /* always clean up */ - crypto_block_cleanup(ctx); + apr_size_t len = *outlen; + + block->f->result->rc = CCCryptorFinal(block->ref, out, + CCCryptorGetOutputLength(block->ref, 0, 1), &len); + + /* always clean up */ + crypto_block_cleanup(block); + + switch (block->f->result->rc) { + case kCCSuccess: { + break; + } + case kCCBufferTooSmall: { + return APR_ENOSPACE; + } + case kCCAlignmentError: { + return APR_EPADDING; + } + case kCCDecodeError: { + return APR_ECRYPT; + } + default: { + return APR_ECRYPT; + } + } + *outlen = len; + + return APR_SUCCESS; - switch (ctx->f->result->rc) { - case kCCSuccess: { - break; - } - case kCCBufferTooSmall: { - return APR_ENOSPACE; - } - case kCCAlignmentError: { - return APR_EPADDING; - } - case kCCDecodeError: { - return APR_ECRYPT; } default: { - return APR_ECRYPT; + + return APR_EINVAL; + } } - *outlen = len; - - return APR_SUCCESS; } @@ -717,55 +966,69 @@ 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_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; + switch (key->rec->ktype) { - apr_pool_cleanup_register(p, block, crypto_block_cleanup_helper, - apr_pool_cleanup_null); + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { - /* generate an IV, if necessary */ - if (key->ivSize) { - if (iv == NULL) { - return APR_ENOIV; + 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); + + /* generate an IV, if necessary */ + if (key->ivSize) { + if (iv == NULL) { + return APR_ENOIV; + } } - } - /* create a new context for decryption */ - switch ((block->f->result->rc = CCCryptorCreate(kCCDecrypt, key->algorithm, - key->options, key->key, key->keyLen, iv, &block->ref))) { - case kCCSuccess: { - break; - } - case kCCParamError: { - return APR_EINIT; - } - case kCCMemoryFailure: { - return APR_ENOMEM; - } - case kCCAlignmentError: { - return APR_EPADDING; - } - case kCCUnimplemented: { - return APR_ENOTIMPL; + /* create a new context for decryption */ + switch ((block->f->result->rc = CCCryptorCreate(kCCDecrypt, key->algorithm, + key->options, key->key, key->keyLen, iv, &block->ref))) { + case kCCSuccess: { + break; + } + case kCCParamError: { + return APR_EINIT; + } + case kCCMemoryFailure: { + return APR_ENOMEM; + } + case kCCAlignmentError: { + return APR_EPADDING; + } + case kCCUnimplemented: { + return APR_ENOTIMPL; + } + default: { + return APR_EINIT; + } + } + + if (blockSize) { + *blockSize = key->blockSize; + } + + return APR_SUCCESS; + } default: { - return APR_EINIT; - } - } - if (blockSize) { - *blockSize = key->blockSize; - } + return APR_EINVAL; - return APR_SUCCESS; + } + } } @@ -789,43 +1052,56 @@ static apr_status_t crypto_block_decrypt_init(apr_crypto_block_t **ctx, */ 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 *ctx) + apr_crypto_block_t *block) { - apr_size_t outl = *outlen; - unsigned char *buffer; + switch (block->key->rec->ktype) { - /* are we after the maximum size of the out buffer? */ - if (!out) { - *outlen = CCCryptorGetOutputLength(ctx->ref, inlen, 1); - return APR_SUCCESS; - } + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { - /* must we allocate the output buffer from a pool? */ - if (!*out) { - outl = CCCryptorGetOutputLength(ctx->ref, inlen, 1); - buffer = apr_palloc(ctx->pool, outl); - if (!buffer) { - return APR_ENOMEM; + apr_size_t outl = *outlen; + unsigned char *buffer; + + /* are we after the maximum size of the out buffer? */ + if (!out) { + *outlen = CCCryptorGetOutputLength(block->ref, inlen, 1); + return APR_SUCCESS; } - apr_crypto_clear(ctx->pool, buffer, outl); - *out = buffer; - } - switch ((ctx->f->result->rc = CCCryptorUpdate(ctx->ref, in, inlen, (*out), - outl, &outl))) { - case kCCSuccess: { - break; - } - case kCCBufferTooSmall: { - return APR_ENOSPACE; + /* must we allocate the output buffer from a pool? */ + if (!*out) { + outl = CCCryptorGetOutputLength(block->ref, inlen, 1); + buffer = apr_palloc(block->pool, outl); + if (!buffer) { + return APR_ENOMEM; + } + apr_crypto_clear(block->pool, buffer, outl); + *out = buffer; + } + + switch ((block->f->result->rc = CCCryptorUpdate(block->ref, in, inlen, (*out), + outl, &outl))) { + case kCCSuccess: { + break; + } + case kCCBufferTooSmall: { + return APR_ENOSPACE; + } + default: { + return APR_ECRYPT; + } + } + *outlen = outl; + + return APR_SUCCESS; + } default: { - return APR_ECRYPT; + + return APR_EINVAL; + } } - *outlen = outl; - - return APR_SUCCESS; } @@ -848,37 +1124,333 @@ static apr_status_t crypto_block_decrypt(unsigned char **out, * @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 *ctx) + apr_size_t *outlen, apr_crypto_block_t *block) { - apr_size_t len = *outlen; + switch (block->key->rec->ktype) { + + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { + + apr_size_t len = *outlen; - ctx->f->result->rc = CCCryptorFinal(ctx->ref, out, - CCCryptorGetOutputLength(ctx->ref, 0, 1), &len); + block->f->result->rc = CCCryptorFinal(block->ref, out, + CCCryptorGetOutputLength(block->ref, 0, 1), &len); - /* always clean up */ - crypto_block_cleanup(ctx); + /* always clean up */ + crypto_block_cleanup(block); + + switch (block->f->result->rc) { + case kCCSuccess: { + break; + } + case kCCBufferTooSmall: { + return APR_ENOSPACE; + } + case kCCAlignmentError: { + return APR_EPADDING; + } + case kCCDecodeError: { + return APR_ECRYPT; + } + default: { + return APR_ECRYPT; + } + } + *outlen = len; + + return APR_SUCCESS; + + } + default: { + + return APR_EINVAL; + + } + } + +} + +static apr_status_t crypto_digest_init(apr_crypto_digest_t **ctx, + const apr_crypto_key_t *key, apr_crypto_digest_rec_t *rec, apr_pool_t *p) +{ + + apr_crypto_digest_t *digest = *ctx; + + if (!digest) { + *ctx = 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; + + apr_pool_cleanup_register(p, digest, crypto_digest_cleanup_helper, + apr_pool_cleanup_null); + + switch (digest->key->rec->ktype) { + + case APR_CRYPTO_KTYPE_HASH: { + + switch (key->rec->k.hash.digest) { + case APR_CRYPTO_DIGEST_MD5: + digest->hash = apr_pcalloc(p, sizeof(CC_MD5_CTX)); + CC_MD5_Init(digest->hash); + break; + case APR_CRYPTO_DIGEST_SHA1: + digest->hash = apr_pcalloc(p, sizeof(CC_SHA1_CTX)); + CC_SHA1_Init(digest->hash); + break; + case APR_CRYPTO_DIGEST_SHA224: + digest->hash = apr_pcalloc(p, sizeof(CC_SHA256_CTX)); + CC_SHA224_Init(digest->hash); + break; + case APR_CRYPTO_DIGEST_SHA256: + digest->hash = apr_pcalloc(p, sizeof(CC_SHA256_CTX)); + CC_SHA256_Init(digest->hash); + break; + case APR_CRYPTO_DIGEST_SHA384: + digest->hash = apr_pcalloc(p, sizeof(CC_SHA512_CTX)); + CC_SHA384_Init(digest->hash); + break; + case APR_CRYPTO_DIGEST_SHA512: + digest->hash = apr_pcalloc(p, sizeof(CC_SHA512_CTX)); + CC_SHA512_Init(digest->hash); + break; + default: + return APR_ENODIGEST; + } - switch (ctx->f->result->rc) { - case kCCSuccess: { break; } - case kCCBufferTooSmall: { - return APR_ENOSPACE; + case APR_CRYPTO_KTYPE_HMAC: { + + digest->hmac = apr_pcalloc(p, sizeof(CCHmacContext)); + if (!digest->hmac) { + return APR_ENOMEM; + } + + CCHmacInit(digest->hmac, key->hmac, key->rec->k.hmac.secret, + key->rec->k.hmac.secretLen); + + break; } - case kCCAlignmentError: { - return APR_EPADDING; + + case APR_CRYPTO_KTYPE_CMAC: { + + return APR_ENOTIMPL; + } - case kCCDecodeError: { - return APR_ECRYPT; + + 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: { + + switch (digest->key->rec->k.hash.digest) { + case APR_CRYPTO_DIGEST_MD5: + CC_MD5_Update(digest->hash, in, inlen); + break; + case APR_CRYPTO_DIGEST_SHA1: + CC_SHA1_Update(digest->hash, in, inlen); + break; + case APR_CRYPTO_DIGEST_SHA224: + CC_SHA224_Update(digest->hash, in, inlen); + break; + case APR_CRYPTO_DIGEST_SHA256: + CC_SHA256_Update(digest->hash, in, inlen); + break; + case APR_CRYPTO_DIGEST_SHA384: + CC_SHA384_Update(digest->hash, in, inlen); + break; + case APR_CRYPTO_DIGEST_SHA512: + CC_SHA512_Update(digest->hash, in, inlen); + break; + default: + return APR_ENODIGEST; + } + + break; + } + case APR_CRYPTO_KTYPE_HMAC: { + + CCHmacUpdate(digest->hmac, in, inlen); + + break; + } + + case APR_CRYPTO_KTYPE_CMAC: { + + return APR_ENOTIMPL; + + } + default: { - return APR_ECRYPT; + + return APR_EINVAL; + } } - *outlen = len; return APR_SUCCESS; +} + +static apr_status_t crypto_digest_final(apr_crypto_digest_t *digest) +{ + + switch (digest->key->rec->ktype) { + + case APR_CRYPTO_KTYPE_HASH: { + + size_t len = digest->key->digestSize; + + /* 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); + } + + switch (digest->key->rec->k.hash.digest) { + case APR_CRYPTO_DIGEST_MD5: + CC_MD5_Final(digest->rec->d.hash.s, digest->hash); + break; + case APR_CRYPTO_DIGEST_SHA1: + CC_SHA1_Final(digest->rec->d.hash.s, digest->hash); + break; + case APR_CRYPTO_DIGEST_SHA224: + CC_SHA224_Final(digest->rec->d.hash.s, digest->hash); + break; + case APR_CRYPTO_DIGEST_SHA256: + CC_SHA256_Final(digest->rec->d.hash.s, digest->hash); + break; + case APR_CRYPTO_DIGEST_SHA384: + CC_SHA384_Final(digest->rec->d.hash.s, digest->hash); + break; + case APR_CRYPTO_DIGEST_SHA512: + CC_SHA512_Final(digest->rec->d.hash.s, digest->hash); + break; + default: + return APR_ENODIGEST; + } + + break; + } + case APR_CRYPTO_KTYPE_HMAC: { + + apr_status_t status = APR_SUCCESS; + + size_t len = digest->key->digestSize; + + 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 */ + CCHmacFinal(digest->hmac, digest->rec->d.sign.s); + + 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 */ + CCHmacFinal(digest->hmac, digest->rec->d.verify.s); + + if (digest->rec->d.verify.slen + == digest->rec->d.verify.vlen) { + status = + apr_crypto_equals(digest->rec->d.verify.s, + digest->rec->d.verify.v, + digest->rec->d.verify.slen) ? + APR_SUCCESS : APR_ENOVERIFY; + } else { + status = APR_ENOVERIFY; + } + + break; + } + default: { + status = APR_ENODIGEST; + break; + } + } + + return status; + + } + + case APR_CRYPTO_KTYPE_CMAC: { + + return APR_ENOTIMPL; + + } + + default: { + + return APR_EINVAL; + + } + } + + return APR_SUCCESS; +} + +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; } /** @@ -886,11 +1458,14 @@ static apr_status_t crypto_block_decrypt_finish(unsigned char *out, */ APR_MODULE_DECLARE_DATA const apr_crypto_driver_t apr_crypto_commoncrypto_driver = { - "commoncrypto", crypto_init, crypto_make, crypto_get_block_key_types, + "commoncrypto", 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_block_cleanup, + 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 }; diff --git a/crypto/apr_crypto_nss.c b/crypto/apr_crypto_nss.c index 52898f69d16..37ffe086231 100644 --- a/crypto/apr_crypto_nss.c +++ b/crypto/apr_crypto_nss.c @@ -51,6 +51,7 @@ struct apr_crypto_t { const apr_crypto_driver_t *provider; apu_err_t *result; apr_crypto_config_t *config; + apr_hash_t *digests; apr_hash_t *types; apr_hash_t *modes; }; @@ -63,8 +64,11 @@ 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; CK_MECHANISM_TYPE cipherMech; + CK_MECHANISM_TYPE hashMech; SECOidTag cipherOid; + SECOidTag hashAlg; PK11SymKey *symKey; int ivSize; int keyLength; @@ -75,11 +79,30 @@ struct apr_crypto_block_t { const apr_crypto_driver_t *provider; const apr_crypto_t *f; PK11Context *ctx; - apr_crypto_key_t *key; + const apr_crypto_key_t *key; SECItem *secParam; int blockSize; }; +struct apr_crypto_digest_t { + apr_pool_t *pool; + const apr_crypto_driver_t *provider; + const apr_crypto_t *f; + apr_crypto_digest_rec_t *rec; + PK11Context *ctx; + const apr_crypto_key_t *key; + SECItem *secParam; +}; + +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 }, @@ -112,16 +135,128 @@ static apr_status_t crypto_error(const apu_err_t **result, */ static apr_status_t crypto_shutdown(void) { - return apr_crypto_lib_term("nss"); + if (NSS_IsInitialized()) { + SECStatus s = NSS_Shutdown(); + if (s != SECSuccess) { + fprintf(stderr, "NSS failed to shutdown, possible leak: %d: %s", + PR_GetError(), PR_ErrorToName(s)); + return APR_EINIT; + } + } + 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) + const apu_err_t **result) { - return apr_crypto_lib_init("nss", params, result, pool); + SECStatus s; + const char *dir = NULL; + const char *keyPrefix = NULL; + const char *certPrefix = NULL; + const char *secmod = NULL; + int noinit = 0; + PRUint32 flags = 0; + + struct { + const char *field; + const char *value; + int set; + } fields[] = { + { "dir", NULL, 0 }, + { "key3", NULL, 0 }, + { "cert7", NULL, 0 }, + { "secmod", NULL, 0 }, + { "noinit", 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 (klen && !strcasecmp(fields[j].field, elt)) { + fields[j].set = 1; + if (ptr) { + fields[j].value = ptr; + } + break; + } + } + + i++; + } + dir = fields[0].value; + keyPrefix = fields[1].value; + certPrefix = fields[2].value; + secmod = fields[3].value; + noinit = fields[4].set; + } + + /* if we've been asked to bypass, do so here */ + if (noinit) { + return APR_SUCCESS; + } + + /* sanity check - we can only initialise NSS once */ + if (NSS_IsInitialized()) { + return APR_EREINIT; + } + + if (keyPrefix || certPrefix || secmod) { + s = NSS_Initialize(dir, certPrefix, keyPrefix, secmod, flags); + } + else if (dir) { + s = NSS_InitReadWrite(dir); + } + else { + s = NSS_NoDB_Init(NULL); + } + if (s != SECSuccess) { + if (result) { + /* Note: all memory must be owned by the caller, in case we're unloaded */ + apu_err_t *err = apr_pcalloc(pool, sizeof(apu_err_t)); + err->rc = PR_GetError(); + err->msg = apr_pstrdup(pool, PR_ErrorToName(s)); + err->reason = apr_pstrdup(pool, "Error during 'nss' initialisation"); + *result = err; + } + + return APR_ECRYPT; + } + + apr_pool_cleanup_register(pool, pool, crypto_shutdown_helper, + apr_pool_cleanup_null); + + return APR_SUCCESS; + } /** @@ -147,12 +282,41 @@ static apr_status_t crypto_block_cleanup(apr_crypto_block_t *block) } +/** + * @brief Clean sign / verify 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_digest_cleanup(apr_crypto_digest_t *digest) +{ + + if (digest->secParam) { + SECITEM_FreeItem(digest->secParam, PR_TRUE); + digest->secParam = NULL; + } + + if (digest->ctx) { + PK11_DestroyContext(digest->ctx, PR_TRUE); + digest->ctx = NULL; + } + + 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); } +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); +} + static apr_status_t crypto_key_cleanup(void *data) { apr_crypto_key_t *key = data; @@ -197,6 +361,7 @@ static apr_status_t crypto_make(apr_crypto_t **ff, { apr_crypto_config_t *config = NULL; apr_crypto_t *f; + int i; f = apr_pcalloc(pool, sizeof(apr_crypto_t)); if (!f) { @@ -214,21 +379,34 @@ static apr_status_t crypto_make(apr_crypto_t **ff, return APR_ENOMEM; } + f->digests = apr_hash_make(pool); + if (!f->digests) { + return APR_ENOMEM; + } + apr_hash_set(f->digests, "md2", APR_HASH_KEY_STRING, &(key_digests[i = 0])); + apr_hash_set(f->digests, "md4", APR_HASH_KEY_STRING, &(key_digests[++i])); + apr_hash_set(f->digests, "md5", APR_HASH_KEY_STRING, &(key_digests[++i])); + 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[0])); - apr_hash_set(f->types, "aes128", APR_HASH_KEY_STRING, &(key_types[1])); - apr_hash_set(f->types, "aes192", APR_HASH_KEY_STRING, &(key_types[2])); - apr_hash_set(f->types, "aes256", APR_HASH_KEY_STRING, &(key_types[3])); + 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[0])); - apr_hash_set(f->modes, "cbc", APR_HASH_KEY_STRING, &(key_modes[1])); + 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])); apr_pool_cleanup_register(pool, f, crypto_cleanup_helper, apr_pool_cleanup_null); @@ -237,6 +415,21 @@ static apr_status_t crypto_make(apr_crypto_t **ff, } +/** + * @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. @@ -385,19 +578,21 @@ static apr_status_t crypto_key(apr_crypto_key_t **k, apr_pool_cleanup_null); } + key->pool = p; key->f = f; key->provider = f->provider; - - /* decide on what cipher mechanism we will be using */ - rv = crypto_cipher_mechanism(key, rec->type, rec->mode, rec->pad); - if (APR_SUCCESS != rv) { - return rv; - } + 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); + if (APR_SUCCESS != rv) { + return rv; + } + /* Turn the raw passphrase and salt into SECItems */ passItem.data = (unsigned char*) rec->k.passphrase.pass; passItem.len = rec->k.passphrase.passLen; @@ -419,11 +614,27 @@ static apr_status_t crypto_key(apr_crypto_key_t **k, SECOID_DestroyAlgorithmID(algid, PR_TRUE); } + /* sanity check? */ + if (!key->symKey) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + f->result->rc = perr; + f->result->msg = PR_ErrorToName(perr); + rv = 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); + if (APR_SUCCESS != rv) { + return rv; + } + /* * NSS is by default in FIPS mode, which disallows the use of unencrypted * symmetrical keys. As per http://permalink.gmane.org/gmane.comp.mozilla.crypto/7947 @@ -505,24 +716,115 @@ static apr_status_t crypto_key(apr_crypto_key_t **k, PK11_FreeSlot(slot); } + /* sanity check? */ + if (!key->symKey) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + f->result->rc = perr; + f->result->msg = PR_ErrorToName(perr); + rv = APR_ENOKEY; + } + } + break; } - default: { + case APR_CRYPTO_KTYPE_HASH: { + + switch (rec->k.hash.digest) { + case APR_CRYPTO_DIGEST_MD5: + key->hashAlg = SEC_OID_MD5; + break; + case APR_CRYPTO_DIGEST_SHA1: + key->hashAlg = SEC_OID_SHA1; + break; + case APR_CRYPTO_DIGEST_SHA224: + key->hashAlg = SEC_OID_SHA224; + break; + case APR_CRYPTO_DIGEST_SHA256: + key->hashAlg = SEC_OID_SHA256; + break; + case APR_CRYPTO_DIGEST_SHA384: + key->hashAlg = SEC_OID_SHA384; + break; + case APR_CRYPTO_DIGEST_SHA512: + key->hashAlg = SEC_OID_SHA512; + break; + default: + return APR_ENODIGEST; + } - return APR_ENOKEY; + break; + } + case APR_CRYPTO_KTYPE_HMAC: { + + /* decide on what cipher mechanism we will be using */ + rv = crypto_cipher_mechanism(key, rec->type, rec->mode, rec->pad); + if (APR_SUCCESS != rv) { + return rv; + } + + switch (rec->k.hmac.digest) { + case APR_CRYPTO_DIGEST_MD5: + key->hashMech = CKM_MD5_HMAC; + break; + case APR_CRYPTO_DIGEST_SHA1: + key->hashMech = CKM_SHA_1_HMAC; + break; + case APR_CRYPTO_DIGEST_SHA224: + key->hashMech = CKM_SHA224_HMAC; + break; + case APR_CRYPTO_DIGEST_SHA256: + key->hashMech = CKM_SHA256_HMAC; + break; + case APR_CRYPTO_DIGEST_SHA384: + key->hashMech = CKM_SHA384_HMAC; + break; + case APR_CRYPTO_DIGEST_SHA512: + key->hashMech = CKM_SHA512_HMAC; + break; + default: + return APR_ENODIGEST; + } + + /* generate the key */ + slot = PK11_GetBestSlot(key->hashMech, NULL); + if (slot) { + + /* prepare the key to wrap */ + secretItem.data = (unsigned char *) rec->k.hmac.secret; + secretItem.len = rec->k.hmac.secretLen; + + key->symKey = PK11_ImportSymKey(slot, key->hashMech, PK11_OriginDerive, + CKA_SIGN, &secretItem, NULL); + /* sanity check? */ + if (!key->symKey) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + f->result->rc = perr; + f->result->msg = PR_ErrorToName(perr); + rv = APR_ENOKEY; + } + } + + PK11_FreeSlot(slot); + } + + break; } + + case APR_CRYPTO_KTYPE_CMAC: { + + return APR_ENOTIMPL; + } - /* sanity check? */ - if (!key->symKey) { - PRErrorCode perr = PORT_GetError(); - if (perr) { - f->result->rc = perr; - f->result->msg = PR_ErrorToName(perr); - rv = APR_ENOKEY; - } + default: { + + return APR_ENOKEY; + + } } return rv; @@ -569,6 +871,7 @@ static apr_status_t crypto_passphrase(apr_crypto_key_t **k, apr_size_t *ivSize, SECAlgorithmID *algid; void *wincx = NULL; /* what is wincx? */ apr_crypto_key_t *key = *k; + apr_crypto_key_rec_t *rec; if (!key) { *k = key = apr_pcalloc(p, sizeof *key); @@ -581,6 +884,11 @@ static apr_status_t crypto_passphrase(apr_crypto_key_t **k, apr_size_t *ivSize, 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); @@ -658,54 +966,68 @@ static apr_status_t crypto_block_encrypt_init(apr_crypto_block_t **ctx, 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); - if (key->ivSize) { - if (iv == NULL) { - return APR_ENOIV; - } - if (*iv == NULL) { - SECStatus s; - usedIv = apr_pcalloc(p, key->ivSize); - if (!usedIv) { - return APR_ENOMEM; - } - apr_crypto_clear(p, usedIv, key->ivSize); - s = PK11_GenerateRandom(usedIv, key->ivSize); - if (s != SECSuccess) { + switch (key->rec->ktype) { + + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { + + if (key->ivSize) { + if (iv == NULL) { return APR_ENOIV; } - *iv = usedIv; + if (*iv == NULL) { + SECStatus s; + usedIv = apr_pcalloc(p, key->ivSize); + if (!usedIv) { + return APR_ENOMEM; + } + apr_crypto_clear(p, usedIv, key->ivSize); + s = PK11_GenerateRandom(usedIv, key->ivSize); + if (s != SECSuccess) { + return APR_ENOIV; + } + *iv = usedIv; + } + else { + usedIv = (unsigned char *) *iv; + } + ivItem.data = usedIv; + ivItem.len = key->ivSize; + block->secParam = PK11_ParamFromIV(key->cipherMech, &ivItem); } else { - usedIv = (unsigned char *) *iv; + block->secParam = PK11_GenerateNewParam(key->cipherMech, key->symKey); + } + block->blockSize = PK11_GetBlockSize(key->cipherMech, block->secParam); + block->ctx = PK11_CreateContextBySymKey(key->cipherMech, CKA_ENCRYPT, + key->symKey, block->secParam); + + /* did an error occur? */ + perr = PORT_GetError(); + if (perr || !block->ctx) { + key->f->result->rc = perr; + key->f->result->msg = PR_ErrorToName(perr); + return APR_EINIT; } - ivItem.data = usedIv; - ivItem.len = key->ivSize; - block->secParam = PK11_ParamFromIV(key->cipherMech, &ivItem); - } - else { - block->secParam = PK11_GenerateNewParam(key->cipherMech, key->symKey); - } - block->blockSize = PK11_GetBlockSize(key->cipherMech, block->secParam); - block->ctx = PK11_CreateContextBySymKey(key->cipherMech, CKA_ENCRYPT, - key->symKey, block->secParam); - /* did an error occur? */ - perr = PORT_GetError(); - if (perr || !block->ctx) { - key->f->result->rc = perr; - key->f->result->msg = PR_ErrorToName(perr); - return APR_EINIT; - } + if (blockSize) { + *blockSize = PK11_GetBlockSize(key->cipherMech, block->secParam); + } + + return APR_SUCCESS; - if (blockSize) { - *blockSize = PK11_GetBlockSize(key->cipherMech, block->secParam); } + default: { - return APR_SUCCESS; + return APR_EINVAL; + + } + } } @@ -731,36 +1053,48 @@ 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) { - unsigned char *buffer; - int outl = (int) *outlen; - SECStatus s; - if (!out) { - *outlen = inlen + block->blockSize; - return APR_SUCCESS; - } - if (!*out) { - buffer = apr_palloc(block->pool, inlen + block->blockSize); - if (!buffer) { - return APR_ENOMEM; + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { + + unsigned char *buffer; + int outl = (int) *outlen; + SECStatus s; + if (!out) { + *outlen = inlen + block->blockSize; + return APR_SUCCESS; + } + if (!*out) { + buffer = apr_palloc(block->pool, inlen + block->blockSize); + if (!buffer) { + return APR_ENOMEM; + } + apr_crypto_clear(block->pool, buffer, inlen + block->blockSize); + *out = buffer; } - apr_crypto_clear(block->pool, buffer, inlen + block->blockSize); - *out = buffer; - } - s = PK11_CipherOp(block->ctx, *out, &outl, inlen, (unsigned char*) in, - inlen); - if (s != SECSuccess) { - PRErrorCode perr = PORT_GetError(); - if (perr) { - block->f->result->rc = perr; - block->f->result->msg = PR_ErrorToName(perr); + s = PK11_CipherOp(block->ctx, *out, &outl, inlen, (unsigned char*) in, + inlen); + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + block->f->result->rc = perr; + block->f->result->msg = PR_ErrorToName(perr); + } + return APR_ECRYPT; } - return APR_ECRYPT; + *outlen = outl; + + return APR_SUCCESS; + } - *outlen = outl; + default: { - return APR_SUCCESS; + return APR_EINVAL; + + } + } } @@ -785,24 +1119,36 @@ static apr_status_t crypto_block_encrypt(unsigned char **out, 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) { - apr_status_t rv = APR_SUCCESS; - unsigned int outl = *outlen; + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { - SECStatus s = PK11_DigestFinal(block->ctx, out, &outl, block->blockSize); - *outlen = outl; + apr_status_t rv = APR_SUCCESS; + unsigned int outl = *outlen; - if (s != SECSuccess) { - PRErrorCode perr = PORT_GetError(); - if (perr) { - block->f->result->rc = perr; - block->f->result->msg = PR_ErrorToName(perr); + SECStatus s = PK11_DigestFinal(block->ctx, out, &outl, block->blockSize); + *outlen = outl; + + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + block->f->result->rc = perr; + block->f->result->msg = PR_ErrorToName(perr); + } + rv = APR_ECRYPT; } - rv = APR_ECRYPT; + crypto_block_cleanup(block); + + return rv; + } - crypto_block_cleanup(block); + default: { - return rv; + return APR_EINVAL; + + } + } } @@ -825,50 +1171,64 @@ 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) { - PRErrorCode perr; - 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; + switch (key->rec->ktype) { - apr_pool_cleanup_register(p, block, crypto_block_cleanup_helper, - apr_pool_cleanup_null); + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { - if (key->ivSize) { - SECItem ivItem; - if (iv == NULL) { - return APR_ENOIV; /* Cannot initialise without an IV */ + PRErrorCode perr; + 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); + + if (key->ivSize) { + SECItem ivItem; + if (iv == NULL) { + return APR_ENOIV; /* Cannot initialise without an IV */ + } + ivItem.data = (unsigned char*) iv; + ivItem.len = key->ivSize; + block->secParam = PK11_ParamFromIV(key->cipherMech, &ivItem); + } + else { + block->secParam = PK11_GenerateNewParam(key->cipherMech, key->symKey); + } + block->blockSize = PK11_GetBlockSize(key->cipherMech, block->secParam); + block->ctx = PK11_CreateContextBySymKey(key->cipherMech, CKA_DECRYPT, + key->symKey, block->secParam); + + /* did an error occur? */ + perr = PORT_GetError(); + if (perr || !block->ctx) { + key->f->result->rc = perr; + key->f->result->msg = PR_ErrorToName(perr); + return APR_EINIT; } - ivItem.data = (unsigned char*) iv; - ivItem.len = key->ivSize; - block->secParam = PK11_ParamFromIV(key->cipherMech, &ivItem); - } - else { - block->secParam = PK11_GenerateNewParam(key->cipherMech, key->symKey); - } - block->blockSize = PK11_GetBlockSize(key->cipherMech, block->secParam); - block->ctx = PK11_CreateContextBySymKey(key->cipherMech, CKA_DECRYPT, - key->symKey, block->secParam); - /* did an error occur? */ - perr = PORT_GetError(); - if (perr || !block->ctx) { - key->f->result->rc = perr; - key->f->result->msg = PR_ErrorToName(perr); - return APR_EINIT; - } + if (blockSize) { + *blockSize = PK11_GetBlockSize(key->cipherMech, block->secParam); + } + + return APR_SUCCESS; - if (blockSize) { - *blockSize = PK11_GetBlockSize(key->cipherMech, block->secParam); } + default: { - return APR_SUCCESS; + return APR_EINVAL; + + } + } } @@ -894,36 +1254,48 @@ 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) { - unsigned char *buffer; - int outl = (int) *outlen; - SECStatus s; - if (!out) { - *outlen = inlen + block->blockSize; - return APR_SUCCESS; - } - if (!*out) { - buffer = apr_palloc(block->pool, inlen + block->blockSize); - if (!buffer) { - return APR_ENOMEM; + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { + + unsigned char *buffer; + int outl = (int) *outlen; + SECStatus s; + if (!out) { + *outlen = inlen + block->blockSize; + return APR_SUCCESS; + } + if (!*out) { + buffer = apr_palloc(block->pool, inlen + block->blockSize); + if (!buffer) { + return APR_ENOMEM; + } + apr_crypto_clear(block->pool, buffer, inlen + block->blockSize); + *out = buffer; } - apr_crypto_clear(block->pool, buffer, inlen + block->blockSize); - *out = buffer; - } - s = PK11_CipherOp(block->ctx, *out, &outl, inlen, (unsigned char*) in, - inlen); - if (s != SECSuccess) { - PRErrorCode perr = PORT_GetError(); - if (perr) { - block->f->result->rc = perr; - block->f->result->msg = PR_ErrorToName(perr); + s = PK11_CipherOp(block->ctx, *out, &outl, inlen, (unsigned char*) in, + inlen); + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + block->f->result->rc = perr; + block->f->result->msg = PR_ErrorToName(perr); + } + return APR_ECRYPT; } - return APR_ECRYPT; + *outlen = outl; + + return APR_SUCCESS; + } - *outlen = outl; + default: { - return APR_SUCCESS; + return APR_EINVAL; + + } + } } @@ -948,37 +1320,326 @@ static apr_status_t crypto_block_decrypt(unsigned char **out, 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) { - apr_status_t rv = APR_SUCCESS; - unsigned int outl = *outlen; + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { - SECStatus s = PK11_DigestFinal(block->ctx, out, &outl, block->blockSize); - *outlen = outl; + apr_status_t rv = APR_SUCCESS; + unsigned int outl = *outlen; - if (s != SECSuccess) { - PRErrorCode perr = PORT_GetError(); - if (perr) { - block->f->result->rc = perr; - block->f->result->msg = PR_ErrorToName(perr); + SECStatus s = PK11_DigestFinal(block->ctx, out, &outl, block->blockSize); + *outlen = outl; + + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + block->f->result->rc = perr; + block->f->result->msg = PR_ErrorToName(perr); + } + rv = APR_ECRYPT; } - rv = APR_ECRYPT; + crypto_block_cleanup(block); + + return rv; + } - crypto_block_cleanup(block); + default: { - return rv; + 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) +{ + PRErrorCode perr; + SECStatus s; + 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; + + apr_pool_cleanup_register(p, digest, crypto_digest_cleanup_helper, + apr_pool_cleanup_null); + + switch (key->rec->ktype) { + + case APR_CRYPTO_KTYPE_HASH: { + + digest->ctx = PK11_CreateDigestContext(key->hashAlg); + + s = PK11_DigestBegin(digest->ctx); + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + digest->f->result->rc = perr; + digest->f->result->msg = PR_ErrorToName(perr); + } + return APR_ECRYPT; + } + + return APR_SUCCESS; + + } + case APR_CRYPTO_KTYPE_HMAC: { + + digest->secParam = PK11_GenerateNewParam(key->cipherMech, key->symKey); + digest->ctx = PK11_CreateContextBySymKey(key->hashMech, CKA_SIGN, + key->symKey, digest->secParam); + + /* did an error occur? */ + perr = PORT_GetError(); + if (perr || !digest->ctx) { + key->f->result->rc = perr; + key->f->result->msg = PR_ErrorToName(perr); + return APR_EINIT; + } + + s = PK11_DigestBegin(digest->ctx); + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + digest->f->result->rc = perr; + digest->f->result->msg = PR_ErrorToName(perr); + } + return APR_ECRYPT; + } + + return APR_SUCCESS; + + } + case APR_CRYPTO_KTYPE_CMAC: { + + return APR_ENOTIMPL; + + } + default: { + + return APR_EINVAL; + + } + } + +} + +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: + case APR_CRYPTO_KTYPE_HMAC: { + + SECStatus s; + + s = PK11_DigestOp(digest->ctx, (unsigned char*) in, + inlen); + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + digest->f->result->rc = perr; + digest->f->result->msg = PR_ErrorToName(perr); + } + return APR_ECRYPT; + } + + return APR_SUCCESS; + + } + case APR_CRYPTO_KTYPE_CMAC: { + + return APR_ENOTIMPL; + + } + 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: + case APR_CRYPTO_KTYPE_HMAC: { + + apr_status_t status = APR_SUCCESS; + unsigned int len; + + /* first, determine the signature length */ + SECStatus s = PK11_DigestFinal(digest->ctx, NULL, &len, 0); + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + digest->f->result->rc = perr; + digest->f->result->msg = PR_ErrorToName(perr); + } + status = APR_ECRYPT; + } + else { + + 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 */ + SECStatus s = PK11_DigestFinal(digest->ctx, + digest->rec->d.hash.s, &len, digest->rec->d.hash.slen); + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + digest->f->result->rc = perr; + digest->f->result->msg = PR_ErrorToName(perr); + } + status = APR_ECRYPT; + } + + break; + } + 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 */ + SECStatus s = PK11_DigestFinal(digest->ctx, + digest->rec->d.sign.s, &len, digest->rec->d.sign.slen); + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + digest->f->result->rc = perr; + digest->f->result->msg = PR_ErrorToName(perr); + } + 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 */ + SECStatus s = PK11_DigestFinal(digest->ctx, + digest->rec->d.verify.s, &len, + digest->rec->d.verify.slen); + if (s != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + if (perr) { + digest->f->result->rc = perr; + digest->f->result->msg = PR_ErrorToName(perr); + } + status = APR_ECRYPT; + } else if (digest->rec->d.verify.slen + == digest->rec->d.verify.vlen) { + status = + apr_crypto_equals(digest->rec->d.verify.s, + digest->rec->d.verify.v, + digest->rec->d.verify.slen) ? + APR_SUCCESS : APR_ENOVERIFY; + } else { + status = APR_ENOVERIFY; + } + + break; + } + default: { + status = APR_ENODIGEST; + } + } + + } + + crypto_digest_cleanup(digest); + + return status; + + } + case APR_CRYPTO_KTYPE_CMAC: { + + return APR_ENOTIMPL; + + } + 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; } /** * NSS module. */ APR_MODULE_DECLARE_DATA const apr_crypto_driver_t apr_crypto_nss_driver = { - "nss", crypto_init, crypto_make, crypto_get_block_key_types, + "nss", 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_block_cleanup, crypto_cleanup, crypto_shutdown, crypto_error, + 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 }; diff --git a/crypto/apr_crypto_openssl.c b/crypto/apr_crypto_openssl.c index b7bed223a10..b1b5f9071f8 100644 --- a/crypto/apr_crypto_openssl.c +++ b/crypto/apr_crypto_openssl.c @@ -37,8 +37,20 @@ #define LOG_PREFIX "apr_crypto_openssl: " -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) -#define EVP_CIPHER_CTX_reset EVP_CIPHER_CTX_cleanup +#ifndef APR_USE_OPENSSL_PRE_1_1_API +#if defined(LIBRESSL_VERSION_NUMBER) +/* LibreSSL declares OPENSSL_VERSION_NUMBER == 2.0 but does not include most + * changes from OpenSSL >= 1.1 (new functions, macros, deprecations, ...), so + * we have to work around this... + */ +#define APR_USE_OPENSSL_PRE_1_1_API (1) +#define APR_USE_OPENSSL_PRE_1_1_1_API (1) +#define APR_USE_OPENSSL_PRE_1_0_API (0) +#else +#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) +#define APR_USE_OPENSSL_PRE_1_0_API (OPENSSL_VERSION_NUMBER < 0x10000000L) +#endif #endif struct apr_crypto_t { @@ -48,6 +60,7 @@ struct apr_crypto_t { apr_crypto_config_t *config; apr_hash_t *types; apr_hash_t *modes; + apr_hash_t *digests; }; struct apr_crypto_config_t { @@ -58,7 +71,10 @@ struct apr_crypto_key_t { apr_pool_t *pool; const apr_crypto_driver_t *provider; const apr_crypto_t *f; - const EVP_CIPHER * cipher; + 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; @@ -69,6 +85,7 @@ 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; @@ -76,6 +93,26 @@ struct apr_crypto_block_t { 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; +}; + +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 }, @@ -106,16 +143,87 @@ static apr_status_t crypto_error(const apu_err_t **result, */ static apr_status_t crypto_shutdown(void) { - return apr_crypto_lib_term("openssl"); + ERR_free_strings(); + EVP_cleanup(); + ENGINE_cleanup(); + 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) + const apu_err_t **result) { - return apr_crypto_lib_init("openssl", params, result, pool); +#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(); + + 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 + +#ifdef 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); } /** @@ -128,7 +236,15 @@ static apr_status_t crypto_block_cleanup(apr_crypto_block_t *ctx) { if (ctx->initialised) { - EVP_CIPHER_CTX_free(ctx->cipherCtx); + 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; } @@ -142,6 +258,33 @@ static apr_status_t crypto_block_cleanup_helper(void *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. @@ -250,21 +393,37 @@ static apr_status_t crypto_make(apr_crypto_t **ff, 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[0])); - apr_hash_set(f->types, "aes128", APR_HASH_KEY_STRING, &(key_types[1])); - apr_hash_set(f->types, "aes192", APR_HASH_KEY_STRING, &(key_types[2])); - apr_hash_set(f->types, "aes256", APR_HASH_KEY_STRING, &(key_types[3])); + 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[0])); - apr_hash_set(f->modes, "cbc", APR_HASH_KEY_STRING, &(key_modes[1])); + 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); @@ -285,6 +444,21 @@ static apr_status_t crypto_make(apr_crypto_t **ff, } +/** + * @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. @@ -415,19 +589,24 @@ static apr_status_t crypto_key(apr_crypto_key_t **k, } } + apr_pool_cleanup_register(p, key, crypto_key_cleanup_helper, + apr_pool_cleanup_null); + + key->pool = p; key->f = f; key->provider = f->provider; - - /* 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; - } + 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, @@ -442,6 +621,12 @@ static apr_status_t crypto_key(apr_crypto_key_t **k, 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; @@ -452,6 +637,115 @@ static apr_status_t crypto_key(apr_crypto_key_t **k, 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: { + +#ifndef 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.hmac) { + 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: { @@ -465,7 +759,7 @@ static apr_status_t crypto_key(apr_crypto_key_t **k, /* 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) { + if (APR_MODE_ECB != rec->mode && key->cipher) { key->ivSize = EVP_CIPHER_iv_length(key->cipher); } @@ -507,6 +801,7 @@ static apr_status_t crypto_passphrase(apr_crypto_key_t **k, apr_size_t *ivSize, 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) { @@ -518,6 +813,11 @@ static apr_status_t crypto_passphrase(apr_crypto_key_t **k, apr_size_t *ivSize, 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); @@ -578,59 +878,73 @@ static apr_status_t crypto_block_encrypt_init(apr_crypto_block_t **ctx, 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); - /* create a new context for encryption */ - if (!block->initialised) { - block->cipherCtx = EVP_CIPHER_CTX_new(); - block->initialised = 1; - } + switch (key->rec->ktype) { - /* generate an IV, if necessary */ - usedIv = NULL; - if (key->ivSize) { - if (iv == NULL) { - return APR_ENOIV; + 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; } - 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))) { + + /* generate an IV, if necessary */ + usedIv = NULL; + if (key->ivSize) { + if (iv == NULL) { return APR_ENOIV; } - *iv = usedIv; - } - else { - usedIv = (unsigned char *) *iv; + 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 */ + /* set up our encryption context */ #if CRYPTO_OPENSSL_CONST_BUFFERS - if (!EVP_EncryptInit_ex(block->cipherCtx, key->cipher, config->engine, - key->key, usedIv)) { + 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; - } + return APR_EINIT; + } - /* Clear up any read padding */ - if (!EVP_CIPHER_CTX_set_padding(block->cipherCtx, key->doPad)) { - return APR_EPADDING; - } + /* 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; - if (blockSize) { - *blockSize = EVP_CIPHER_block_size(key->cipher); } + default: { - return APR_SUCCESS; + return APR_EINVAL; + + } + } } @@ -654,39 +968,52 @@ static apr_status_t crypto_block_encrypt_init(apr_crypto_block_t **ctx, */ 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 *ctx) + apr_crypto_block_t *block) { - int outl = *outlen; - unsigned char *buffer; + switch (block->key->rec->ktype) { - /* are we after the maximum size of the out buffer? */ - if (!out) { - *outlen = inlen + EVP_MAX_BLOCK_LENGTH; - return APR_SUCCESS; - } + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { - /* must we allocate the output buffer from a pool? */ - if (!*out) { - buffer = apr_palloc(ctx->pool, inlen + EVP_MAX_BLOCK_LENGTH); - if (!buffer) { - return APR_ENOMEM; + 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; } - apr_crypto_clear(ctx->pool, buffer, inlen + EVP_MAX_BLOCK_LENGTH); - *out = buffer; - } #if CRYPT_OPENSSL_CONST_BUFFERS - if (!EVP_EncryptUpdate(ctx->cipherCtx, (*out), &outl, in, inlen)) { + if (!EVP_EncryptUpdate(block->cipherCtx, (*out), &outl, in, inlen)) { #else - if (!EVP_EncryptUpdate(ctx->cipherCtx, (*out), &outl, - (unsigned char *) in, inlen)) { + if (!EVP_EncryptUpdate(block->cipherCtx, (*out), &outl, + (unsigned char *) in, inlen)) { #endif - EVP_CIPHER_CTX_reset(ctx->cipherCtx); - return APR_ECRYPT; + crypto_block_cleanup(block); + return APR_ECRYPT; + } + *outlen = outl; + + return APR_SUCCESS; + } - *outlen = outl; + default: { - return APR_SUCCESS; + return APR_EINVAL; + + } + } } @@ -709,20 +1036,33 @@ static apr_status_t crypto_block_encrypt(unsigned char **out, * @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 *ctx) + apr_size_t *outlen, apr_crypto_block_t *block) { - apr_status_t rc = APR_SUCCESS; - int len = *outlen; + 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; - if (EVP_EncryptFinal_ex(ctx->cipherCtx, out, &len) == 0) { - rc = APR_EPADDING; - } - else { - *outlen = len; } - EVP_CIPHER_CTX_reset(ctx->cipherCtx); + default: { + + return APR_EINVAL; - return rc; + } + } } @@ -756,43 +1096,57 @@ static apr_status_t crypto_block_decrypt_init(apr_crypto_block_t **ctx, 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); - /* create a new context for encryption */ - if (!block->initialised) { - block->cipherCtx = EVP_CIPHER_CTX_new(); - block->initialised = 1; - } + switch (key->rec->ktype) { - /* generate an IV, if necessary */ - if (key->ivSize) { - if (iv == NULL) { - return APR_ENOIV; + 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; } - } - /* set up our encryption context */ + /* 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)) { + 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)) { + if (!EVP_DecryptInit_ex(block->cipherCtx, key->cipher, config->engine, (unsigned char *) key->key, (unsigned char *) iv)) { #endif - return APR_EINIT; - } + return APR_EINIT; + } - /* Clear up any read padding */ - if (!EVP_CIPHER_CTX_set_padding(block->cipherCtx, key->doPad)) { - return APR_EPADDING; - } + /* 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; - if (blockSize) { - *blockSize = EVP_CIPHER_block_size(key->cipher); } + default: { - return APR_SUCCESS; + return APR_EINVAL; + + } + } } @@ -816,39 +1170,53 @@ static apr_status_t crypto_block_decrypt_init(apr_crypto_block_t **ctx, */ 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 *ctx) + apr_crypto_block_t *block) { - int outl = *outlen; - unsigned char *buffer; + switch (block->key->rec->ktype) { - /* are we after the maximum size of the out buffer? */ - if (!out) { - *outlen = inlen + EVP_MAX_BLOCK_LENGTH; - return APR_SUCCESS; - } + case APR_CRYPTO_KTYPE_PASSPHRASE: + case APR_CRYPTO_KTYPE_SECRET: { - /* must we allocate the output buffer from a pool? */ - if (!(*out)) { - buffer = apr_palloc(ctx->pool, inlen + EVP_MAX_BLOCK_LENGTH); - if (!buffer) { - return APR_ENOMEM; + 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; } - apr_crypto_clear(ctx->pool, buffer, inlen + EVP_MAX_BLOCK_LENGTH); - *out = buffer; - } #if CRYPT_OPENSSL_CONST_BUFFERS - if (!EVP_DecryptUpdate(ctx->cipherCtx, *out, &outl, in, inlen)) { + if (!EVP_DecryptUpdate(block->cipherCtx, *out, &outl, in, inlen)) { #else - if (!EVP_DecryptUpdate(ctx->cipherCtx, *out, &outl, (unsigned char *) in, - inlen)) { + if (!EVP_DecryptUpdate(block->cipherCtx, *out, &outl, (unsigned char *) in, + inlen)) { #endif - EVP_CIPHER_CTX_reset(ctx->cipherCtx); - return APR_ECRYPT; + crypto_block_cleanup(block); + + return APR_ECRYPT; + } + *outlen = outl; + + return APR_SUCCESS; + } - *outlen = outl; + default: { - return APR_SUCCESS; + return APR_EINVAL; + + } + } } @@ -871,33 +1239,279 @@ static apr_status_t crypto_block_decrypt(unsigned char **out, * @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 *ctx) + apr_size_t *outlen, apr_crypto_block_t *block) { - apr_status_t rc = APR_SUCCESS; - int len = *outlen; + 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; - if (EVP_DecryptFinal_ex(ctx->cipherCtx, out, &len) == 0) { - rc = APR_EPADDING; } - else { - *outlen = len; + default: { + + return APR_EINVAL; + } - EVP_CIPHER_CTX_reset(ctx->cipherCtx); + } + +} - return rc; +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; } /** * OpenSSL module. */ APR_MODULE_DECLARE_DATA const apr_crypto_driver_t apr_crypto_openssl_driver = { - "openssl", crypto_init, crypto_make, crypto_get_block_key_types, + "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_block_cleanup, crypto_cleanup, crypto_shutdown, crypto_error, + 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 }; diff --git a/include/apr_crypto.h b/include/apr_crypto.h index bc92b31b5c4..14ea44d51cd 100644 --- a/include/apr_crypto.h +++ b/include/apr_crypto.h @@ -42,17 +42,31 @@ extern "C" { #ifndef APU_CRYPTO_RECOMMENDED_DRIVER #if APU_HAVE_COMMONCRYPTO +/** Recommended driver for this platform */ #define APU_CRYPTO_RECOMMENDED_DRIVER "commoncrypto" -#elif APU_HAVE_OPENSSL +#else +#if APU_HAVE_OPENSSL +/** Recommended driver for this platform */ #define APU_CRYPTO_RECOMMENDED_DRIVER "openssl" -#elif APU_HAVE_NSS +#else +#if APU_HAVE_NSS +/** Recommended driver for this platform */ #define APU_CRYPTO_RECOMMENDED_DRIVER "nss" -#elif APU_HAVE_MSCNG +#else +#if APU_HAVE_MSCNG +/** Recommended driver for this platform */ #define APU_CRYPTO_RECOMMENDED_DRIVER "mscng" -#elif APU_HAVE_MSCAPI +#else +#if APU_HAVE_MSCAPI +/** Recommended driver for this platform */ #define APU_CRYPTO_RECOMMENDED_DRIVER "mscapi" +#else +#endif +#endif +#endif +#endif +#endif #endif -#endif /* APU_CRYPTO_RECOMMENDED_DRIVER */ /** * Symmetric Key types understood by the library. @@ -95,6 +109,9 @@ extern "C" { * aligned data, use 3DES_192/CBC, AES_256/CBC or AES_256/ECB. */ +/** + * Types of ciphers. + */ typedef enum { APR_KEY_NONE, APR_KEY_3DES_192, /** 192 bit (3-Key) 3DES */ @@ -104,6 +121,9 @@ typedef enum /** 256 bit AES */ } apr_crypto_block_key_type_e; +/** + * Types of modes supported by the ciphers. + */ typedef enum { APR_MODE_NONE, /** An error condition */ @@ -112,55 +132,369 @@ typedef enum /** Cipher Block Chaining */ } apr_crypto_block_key_mode_e; -/* These are opaque structs. Instantiation is up to each backend */ +/** + * Types of digests supported by the apr_crypto_key() function. + */ +typedef enum +{ + APR_CRYPTO_DIGEST_NONE, /** An error condition */ + APR_CRYPTO_DIGEST_MD5, /** MD5 */ + APR_CRYPTO_DIGEST_SHA1, /** SHA1 */ + APR_CRYPTO_DIGEST_SHA224, /** SHA224 */ + APR_CRYPTO_DIGEST_SHA256, /** SHA256 */ + APR_CRYPTO_DIGEST_SHA384, /** SHA384 */ + APR_CRYPTO_DIGEST_SHA512, /** SHA512 */ +} apr_crypto_block_key_digest_e; + +/** + * Structure returned by the crypto_get_block_key_digests() function. + */ +typedef struct apr_crypto_block_key_digest_t { + /** The digest used with this crypto operation. */ + apr_crypto_block_key_digest_e type; + /** The digest size used with this digest operation */ + int digestsize; + /** The block size used with this digest operation */ + int blocksize; +} apr_crypto_block_key_digest_t; + +/** + * Structure representing a backend crypto driver. + * + * This structure is created with apr_crypto_get_driver(). + */ typedef struct apr_crypto_driver_t apr_crypto_driver_t; + +/** + * Structure to support a group of crypto operations. + * + * This structure is created with apr_crypto_make(). + */ typedef struct apr_crypto_t apr_crypto_t; + +/** + * Structure representing the configuration of the given backend + * crypto library. + */ typedef struct apr_crypto_config_t apr_crypto_config_t; + +/** + * Structure representing a key prepared for encryption, decryption, + * signing or verifying. + * + * This structure is created using the apr_crypto_key() function. + */ typedef struct apr_crypto_key_t apr_crypto_key_t; + +/** + * Structure representing a block context for encryption, decryption, + * signing or verifying. + * + * This structure is created using the apr_crypto_block_encrypt_init() + * and apr_crypto_block_decrypt_init() functions. + */ typedef struct apr_crypto_block_t apr_crypto_block_t; +/** + * Structure representing a digest context for signing or verifying. + * + * This structure is created using the apr_crypto_digest_init() function. + */ +typedef struct apr_crypto_digest_t apr_crypto_digest_t; + +/** + * Structure returned by the crypto_get_block_key_types() function. + */ typedef struct apr_crypto_block_key_type_t { + /** The cipher used with this crypto operation. */ apr_crypto_block_key_type_e type; + /** The key size used with this crypto operation */ int keysize; + /** The block size used with this crypto operation */ int blocksize; + /** The initialisation vector size used with this crypto operation */ int ivsize; } apr_crypto_block_key_type_t; +/** + * Structure returned by the crypto_get_block_key_modes() function. + */ typedef struct apr_crypto_block_key_mode_t { + /** The mode used with this crypto operation. */ apr_crypto_block_key_mode_e mode; } apr_crypto_block_key_mode_t; +/** + * Structure describing a key to be derived from PBKDF2 to be passed by the + * apr_crypto_key() function. + * + * Derived keys are used for encryption and decryption. + * + * Implementations must use apr_crypto_key_rec_make() to allocate + * this structure. + */ typedef struct apr_crypto_passphrase_t { + /** The passphrase used by the key generation algorithm */ const char *pass; + /** The length of the passphrase */ apr_size_t passLen; + /** The salt used by the key derivation algorithm */ const unsigned char * salt; + /** The length of the salt. */ apr_size_t saltLen; + /** The number of iterations used by the key derivation function */ int iterations; } apr_crypto_passphrase_t; +/** + * Structure describing a raw key to be passed by the + * apr_crypto_key() function. + * + * Raw keys are used for encryption and decryption, and must match + * the correct sizes for each cipher. + * + * Implementations must use apr_crypto_key_rec_make() to allocate + * this structure. + */ typedef struct apr_crypto_secret_t { + /** The raw secret key used for encrypt / decrypt. Must be + * the same size as the block size of the cipher being used. + */ const unsigned char *secret; + /** The length of the secret key. */ apr_size_t secretLen; } apr_crypto_secret_t; +/** + * Structure describing a simple digest hash to be generated by the + * apr_crypto_key() function. + * + * Implementations must use apr_crypto_key_rec_make() to allocate + * this structure. + */ +typedef struct apr_crypto_key_hash_t { + /** The digest used for the HMAC. */ + apr_crypto_block_key_digest_e digest; +} apr_crypto_key_hash_t; + +/** + * Structure describing a HMAC key and digest to be generated by the + * apr_crypto_key() function. + * + * Implementations must use apr_crypto_key_rec_make() to allocate + * this structure. + */ +typedef struct apr_crypto_key_hmac_t { + /** The secret used for the HMAC */ + const unsigned char *secret; + /** The length of the secret used for the HMAC */ + apr_size_t secretLen; + /** The digest used for the HMAC. */ + apr_crypto_block_key_digest_e digest; +} apr_crypto_key_hmac_t; + +/** + * Structure describing a CMAC key and digest to be generated by the + * apr_crypto_key() function. + * + * Implementations must use apr_crypto_key_rec_make() to allocate + * this structure. + */ +typedef struct apr_crypto_key_cmac_t { + /** The secret used for the CMAC */ + const unsigned char *secret; + /** The length of the secret used for the CMAC */ + apr_size_t secretLen; + /** The digest used for the CMAC. */ + apr_crypto_block_key_digest_e digest; +} apr_crypto_key_cmac_t; + +/** + * Structure used to create a hashed digest. + * + * Implementations must use apr_crypto_digest_rec_make() to allocate + * this structure. + */ +typedef struct apr_crypto_digest_hash_t { + /** The message digest */ + unsigned char *s; + /** The length of the message digest */ + apr_size_t slen; + /** The digest algorithm */ + apr_crypto_block_key_digest_e digest; +} apr_crypto_digest_hash_t; + +/** + * Structure used to create a signature. + * + * Implementations must use apr_crypto_digest_rec_make() to allocate + * this structure. + */ +typedef struct apr_crypto_digest_sign_t { + /** The message digest */ + unsigned char *s; + /** The length of the message digest */ + apr_size_t slen; + /** The digest algorithm */ + apr_crypto_block_key_digest_e digest; +} apr_crypto_digest_sign_t; + +/** + * Structure used to create a signature for verification. + * + * Implementations must use apr_crypto_digest_rec_make() to allocate + * this structure. + */ +typedef struct apr_crypto_digest_verify_t { + /** The message digest generated */ + unsigned char *s; + /** The length of the message digest */ + apr_size_t slen; + /** The message digest to be verified against */ + const unsigned char *v; + /** The length of the message digest */ + apr_size_t vlen; + /** The digest algorithm */ + apr_crypto_block_key_digest_e digest; +} apr_crypto_digest_verify_t; + +/** + * Types of keys supported by the apr_crypto_key() function and the + * apr_crypto_key_rec_t structure. + */ typedef enum { - /** Key is derived from a passphrase */ + /** + * Key is derived from a passphrase. + * + * Used with the encrypt / decrypt functions. + */ APR_CRYPTO_KTYPE_PASSPHRASE = 1, - /** Key is derived from a raw key */ - APR_CRYPTO_KTYPE_SECRET = 2, + /** + * Key is derived from a raw key. + * + * Used with the encrypt / decrypt functions. + */ + APR_CRYPTO_KTYPE_SECRET = 2, + /** + * Simple digest, no key. + * + * Used with the digest functions. + */ + APR_CRYPTO_KTYPE_HASH = 3, + /** + * HMAC Key is derived from a raw key. + * + * Used with the digest functions. + */ + APR_CRYPTO_KTYPE_HMAC = 4, + /** + * CMAC Key is derived from a raw key. + * + * Used with the digest functions. + */ + APR_CRYPTO_KTYPE_CMAC = 5, } apr_crypto_key_type; +/** + * Types of digests supported by the apr_crypto_digest() functions and the + * apr_crypto_digest_rec_t structure. + */ +typedef enum { + /** + * Simple digest operation. + * + * Use with apr_crypto_key_rec_t APR_CRYPTO_KTYPE_HASH. + */ + APR_CRYPTO_DTYPE_HASH = 1, + /** + * Sign operation. + * + * Use with apr_crypto_key_rec_t APR_CRYPTO_KTYPE_HMAC or + * APR_CRYPTO_KTYPE_CMAC. + */ + APR_CRYPTO_DTYPE_SIGN = 2, + /** + * Verify operation. + * + * Use with apr_crypto_key_rec_t APR_CRYPTO_KTYPE_HMAC or + * APR_CRYPTO_KTYPE_CMAC. + */ + APR_CRYPTO_DTYPE_VERIFY = 3, +} apr_crypto_digest_type_e; + +/** + * Structure describing a key to be generated by the + * apr_crypto_key() function. + * + * Implementations must use apr_crypto_key_rec_make() to allocate + * this structure. + */ typedef struct apr_crypto_key_rec_t { + /** The type of the key. */ apr_crypto_key_type ktype; + /** The cipher used with this crypto operation. */ apr_crypto_block_key_type_e type; + /** The mode used with this crypto operation. */ apr_crypto_block_key_mode_e mode; + /** Non zero if padding should be used with this crypto operation. */ int pad; + /** Details of each key, based on the key type. */ union { + /** + * This key is generated using a PBE algorithm from a given + * passphrase, and can be used to encrypt / decrypt. + * + * Key type: APR_CRYPTO_KTYPE_PASSPHRASE + */ apr_crypto_passphrase_t passphrase; + /** + * This is a raw key matching the block size of the given + * cipher, and can be used to encrypt / decrypt. + * + * Key type: APR_CRYPTO_KTYPE_SECRET + */ apr_crypto_secret_t secret; + /** + * This represents a simple digest with no key. + * + * Key type: APR_CRYPTO_KTYPE_HASH + */ + apr_crypto_key_hash_t hash; + /** + * This is a key of arbitrary length used with an HMAC. + * + * Key type: APR_CRYPTO_KTYPE_HMAC + */ + apr_crypto_key_hmac_t hmac; + /** + * This is a key of arbitrary length used with a CMAC. + * + * Key type: APR_CRYPTO_KTYPE_CMAC + */ + apr_crypto_key_cmac_t cmac; } k; } apr_crypto_key_rec_t; +/** + * Structure describing a digest to be hashed, signed or verified. + * + * This structure is passed to the apr_crypto_digest_init() and + * apr_crypto_digest() functions. + * + * Implementations must use apr_crypto_digest_rec_make() to allocate + * this structure. + */ +typedef struct apr_crypto_digest_rec_t { + /** The type of the digest record. */ + apr_crypto_digest_type_e dtype; + /** Details of each digest, based on the digest type. */ + union { + apr_crypto_digest_hash_t hash; + apr_crypto_digest_sign_t sign; + apr_crypto_digest_verify_t verify; + } d; +} apr_crypto_digest_rec_t; + /** * @brief Perform once-only initialisation. Call once only. * @@ -229,8 +563,9 @@ APR_DECLARE(int) apr_crypto_equals(const void *buf1, const void *buf2, * @remarks OpenSSL: currently no params are supported. */ APR_DECLARE(apr_status_t) apr_crypto_get_driver( - const apr_crypto_driver_t **driver, const char *name, - const char *params, const apu_err_t **result, apr_pool_t *pool); + const apr_crypto_driver_t **driver, + const char *name, const char *params, const apu_err_t **result, + apr_pool_t *pool); /** * @brief Return the name of the driver. @@ -266,9 +601,21 @@ APR_DECLARE(apr_status_t) apr_crypto_error(const apu_err_t **result, * @remarks OpenSSL: the params can have "engine" as a key, followed by an equal * sign and a value. */ -APR_DECLARE(apr_status_t) - apr_crypto_make(apr_crypto_t **f, const apr_crypto_driver_t *driver, - const char *params, apr_pool_t *pool); +APR_DECLARE(apr_status_t) apr_crypto_make(apr_crypto_t **f, + const apr_crypto_driver_t *driver, const char *params, + apr_pool_t *pool); + +/** + * @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, which in turn begins with an + * integer. + * + * @param digests - hashtable of key digests keyed to constants. + * @param f - encryption context + * @return APR_SUCCESS for success + */ +APR_DECLARE(apr_status_t) apr_crypto_get_block_key_digests(apr_hash_t **digests, + const apr_crypto_t *f); /** * @brief Get a hash table of key types, keyed by the name of the type against @@ -294,21 +641,43 @@ APR_DECLARE(apr_status_t) apr_crypto_get_block_key_types(apr_hash_t **types, APR_DECLARE(apr_status_t) apr_crypto_get_block_key_modes(apr_hash_t **modes, const apr_crypto_t *f); +/** + * @brief Create a key record to be passed to apr_crypto_key(). + * @param ktype The apr_crypto_key_type to use. + * @param p The pool to use. + * @return Returns a blank structure of the correct size. + */ +APR_DECLARE(apr_crypto_key_rec_t *) apr_crypto_key_rec_make( + apr_crypto_key_type ktype, apr_pool_t *p); + +/** + * @brief Create a digest record to be passed to apr_crypto_digest_init(). + * @param dtype The type of digest record to create. + * @param p The pool to use. + * @return Returns a blank structure of the correct size. + */ +APR_DECLARE(apr_crypto_digest_rec_t *) apr_crypto_digest_rec_make( + apr_crypto_digest_type_e dtype, apr_pool_t *p); + /** * @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. + * up when the context is cleaned, and may be reused with multiple + * encryption, decryption, signing or verifying operations. The choice of + * key type much match the intended operation. * @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. + * @return APR_ENOKEY if the pass phrase is missing or empty, or if a backend + * error occurred while generating the key. + * @return APR_ENOCIPHER if the type or mode + * is not supported by the particular backend. + * @return APR_EKEYTYPE if the key type is + * not known. + * @return APR_EPADDING if padding was requested but is not supported. + * @return APR_ENOTIMPL if not implemented. */ APR_DECLARE(apr_status_t) apr_crypto_key(apr_crypto_key_t **key, const apr_crypto_key_rec_t *rec, const apr_crypto_t *f, apr_pool_t *p); @@ -335,11 +704,14 @@ APR_DECLARE(apr_status_t) apr_crypto_key(apr_crypto_key_t **key, * @param iterations Number of iterations to use in algorithm * @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. + * @return APR_ENOKEY if the pass phrase is missing or empty, or if a backend + * error occurred while generating the key. + * @return APR_ENOCIPHER if the type or mode + * is not supported by the particular backend. + * @return APR_EKEYTYPE if the key type is + * not known. + * @return APR_EPADDING if padding was requested but is not supported. + * @return APR_ENOTIMPL if not implemented. * @deprecated Replaced by apr_crypto_key(). */ APR_DECLARE(apr_status_t) apr_crypto_passphrase(apr_crypto_key_t **key, @@ -361,9 +733,10 @@ APR_DECLARE(apr_status_t) apr_crypto_passphrase(apr_crypto_key_t **key, * @param key The key structure to use. * @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. + * @return APR_ENOIV if an initialisation vector is required but not specified. + * @return APR_EINIT if the backend failed to initialise the context. + * @return APR_ENOTIMPL if not implemented. + * @return APR_EINVAL if the key type does not support the given operation. */ APR_DECLARE(apr_status_t) apr_crypto_block_encrypt_init( apr_crypto_block_t **ctx, const unsigned char **iv, @@ -384,8 +757,9 @@ APR_DECLARE(apr_status_t) apr_crypto_block_encrypt_init( * @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. + * @return APR_ECRYPT if an error occurred. + * @return APR_ENOTIMPL if not implemented. + * @return APR_EINVAL if the key type does not support the given operation. */ APR_DECLARE(apr_status_t) apr_crypto_block_encrypt(unsigned char **out, apr_size_t *outlen, const unsigned char *in, apr_size_t inlen, @@ -401,13 +775,14 @@ APR_DECLARE(apr_status_t) apr_crypto_block_encrypt(unsigned char **out, * 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. + * buffer used by apr_crypto_block_encrypt(). 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. + * @return APR_EINVAL if the key type does not support the given operation. */ APR_DECLARE(apr_status_t) apr_crypto_block_encrypt_finish(unsigned char *out, apr_size_t *outlen, apr_crypto_block_t *ctx); @@ -421,9 +796,10 @@ APR_DECLARE(apr_status_t) apr_crypto_block_encrypt_finish(unsigned char *out, * @param iv Optional initialisation vector. * @param key The key structure to use. * @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. + * @return APR_ENOIV if an initialisation vector is required but not specified. + * @return APR_EINIT if the backend failed to initialise the context. + * @return APR_ENOTIMPL if not implemented. + * @return APR_EINVAL if the key type does not support the given operation. */ APR_DECLARE(apr_status_t) apr_crypto_block_decrypt_init( apr_crypto_block_t **ctx, apr_size_t *blockSize, @@ -444,8 +820,9 @@ APR_DECLARE(apr_status_t) apr_crypto_block_decrypt_init( * @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. + * @return APR_ECRYPT if an error occurred. + * @return APR_ENOTIMPL if not implemented. + * @return APR_EINVAL if the key type does not support the given operation. */ APR_DECLARE(apr_status_t) apr_crypto_block_decrypt(unsigned char **out, apr_size_t *outlen, const unsigned char *in, apr_size_t inlen, @@ -461,13 +838,14 @@ APR_DECLARE(apr_status_t) apr_crypto_block_decrypt(unsigned char **out, * 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. + * buffer used by apr_crypto_block_decrypt(). 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. + * @return APR_EINVAL if the key type does not support the given operation. */ APR_DECLARE(apr_status_t) apr_crypto_block_decrypt_finish(unsigned char *out, apr_size_t *outlen, apr_crypto_block_t *ctx); @@ -480,6 +858,103 @@ APR_DECLARE(apr_status_t) apr_crypto_block_decrypt_finish(unsigned char *out, */ APR_DECLARE(apr_status_t) apr_crypto_block_cleanup(apr_crypto_block_t *ctx); +/** + * @brief Initialise a context for hashing, signing or verifying arbitrary + * data. + * + * This function supports: + * - Simple hashing (MD5, SHA1, SHA224, SHA256, SHA384, SHA512). + * - HMAC (with a secret key) + * - CMAC (with a secret key) + * + * Details of the key and the type of digest to be performed are + * passed in the constant apr_crypto_key_t structure, which can be + * reused by many calls to apr_crypto_digest_init(). + * + * Details of this particular operation are read from and written to + * the apr_crypto_digest_rec_t structure, which is expected to + * contain the message digest to be verified, as well as message + * digest generated during the hashing or signing process. This + * structure will be modified by each digest operation, and cannot be + * shared. + * @note If *d is NULL, a apr_crypto_digest_t will be created from a pool. If + * *d is not NULL, *d must point at a previously created structure. + * @param d The digest context returned, see note. + * @param key The key structure to use. + * @param rec The digest record indicating whether we want to sign or verify. + * This record contains digest we want to verify against, as well as + * the signature we have generated. + * @param p The pool to use. + * @return APR_SUCCESS if successful. + * @return APR_ENOIV if an initialisation vector is required but not specified. + * @return APR_EINIT if the backend failed to initialise the context. + * @return APR_ENOTIMPL if not implemented. + * @return APR_EINVAL if the key type does not support the given operation. + */ +APR_DECLARE(apr_status_t) apr_crypto_digest_init(apr_crypto_digest_t **d, + const apr_crypto_key_t *key, apr_crypto_digest_rec_t *rec, apr_pool_t *p); + +/** + * @brief Update the digest with data provided by in. + * @param digest The block context to use. + * @param in Address of the buffer to digest. + * @param inlen Length of the buffer to digest. + * @return APR_SUCCESS if successful. + * @return APR_ECRYPT if an error occurred. + * @return APR_ENOTIMPL if not implemented. + * @return APR_EINVAL if the key type does not support the given operation. + */ +APR_DECLARE(apr_status_t) apr_crypto_digest_update(apr_crypto_digest_t *digest, + const unsigned char *in, apr_size_t inlen); + +/** + * @brief Finalise the digest and write the result. + * + * The result is written to the apr_crypto_digest_rec_t structure + * passed into apr_crypto_digest_init(). + * + * If verification is requested, this function will return the + * result of the verification. + * @note After this call, the context is cleaned and can be reused by + * apr_crypto_digest_init(). + * @param digest The digest context to use. + * @return APR_SUCCESS if hash, signing or verification was successful. + * @return APR_ENOVERIFY if the verification failed. + * @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. + * @return APR_EINVAL if the key type does not support the given operation. + */ +APR_DECLARE(apr_status_t) apr_crypto_digest_final(apr_crypto_digest_t *digest); + +/** + * @brief One shot digest on a single memory buffer. + * @param key The key structure to use. + * @param rec The digest record indicating whether we want to sign or verify. + * This record contains digest we want to verify against, as well as + * the signature we have generated. This record will contain the digest + * calculated. + * @param in Address of the buffer to digest. + * @param inlen Length of the buffer to digest. + * @param p The pool to use. + * @return APR_ENOIV if an initialisation vector is required but not specified. + * @return APR_EINIT if the backend failed to initialise the context. + * @return APR_ENOTIMPL if not implemented. + * @return APR_EINVAL if the key type does not support the given operation. + */ +APR_DECLARE(apr_status_t) apr_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); + +/** + * @brief Clean digest context. + * @note After cleanup, a digest context is free to be reused if necessary. + * @param ctx The digest context to use. + * @return Returns APR_ENOTIMPL if not supported. + */ +APR_DECLARE(apr_status_t) apr_crypto_digest_cleanup(apr_crypto_digest_t *ctx); + /** * @brief Clean encryption / decryption context. * @note After cleanup, a context is free to be reused if necessary. @@ -494,9 +969,8 @@ APR_DECLARE(apr_status_t) apr_crypto_cleanup(apr_crypto_t *f); * @param driver - driver to use * @return Returns APR_ENOTIMPL if not supported. */ -APR_DECLARE(apr_status_t) - apr_crypto_shutdown(const apr_crypto_driver_t *driver); - +APR_DECLARE(apr_status_t) apr_crypto_shutdown( + const apr_crypto_driver_t *driver); #if APU_HAVE_CRYPTO_PRNG diff --git a/include/apu_errno.h b/include/apu_errno.h index 58585b71483..7d967add6f1 100644 --- a/include/apu_errno.h +++ b/include/apu_errno.h @@ -51,6 +51,7 @@ extern "C" { * APR_ENOENGINE The engine provided was not recognised * APR_EINITENGINE The engine could not be initialised * APR_EREINIT Underlying crypto has already been initialised + * APR_ENOVERIFY The signature verification failed * * *
@@ -83,6 +84,8 @@ extern "C" {
 #define APR_EINITENGINE      (APR_UTIL_START_STATUS + 11)
 /** @see APR_STATUS_IS_EREINIT */
 #define APR_EREINIT          (APR_UTIL_START_STATUS + 12)
+/** @see APR_STATUS_IS_ENOVERIFY */
+#define APR_ENOVERIFY        (APR_UTIL_START_STATUS + 13)
 /** @} */
 
 /**
@@ -151,6 +154,10 @@ extern "C" {
  * Crypto has already been initialised
  */
 #define APR_STATUS_IS_EREINIT(s)        ((s) == APR_EREINIT)
+/**
+ * The signature verification failed
+ */
+#define APR_STATUS_IS_ENOVERIFY(s)        ((s) == APR_ENOVERIFY)
 /** @} */
 
 /**
diff --git a/include/private/apr_crypto_internal.h b/include/private/apr_crypto_internal.h
index 77581bd156f..936d6dc504d 100644
--- a/include/private/apr_crypto_internal.h
+++ b/include/private/apr_crypto_internal.h
@@ -57,6 +57,17 @@ struct apr_crypto_driver_t {
     apr_status_t (*make)(apr_crypto_t **f, const apr_crypto_driver_t *provider,
             const char *params, apr_pool_t *pool);
 
+    /**
+     * @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
+     */
+    apr_status_t (*get_block_key_digests)(apr_hash_t **types,
+            const apr_crypto_t *f);
+
     /**
      * @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.
@@ -236,6 +247,64 @@ struct apr_crypto_driver_t {
     apr_status_t (*block_decrypt_finish)(unsigned char *out,
             apr_size_t *outlen, apr_crypto_block_t *ctx);
 
+    /**
+     * @brief Initialise a context for signing or verifying arbitrary data using the
+     *        given key.
+     * @note If *d is NULL, a apr_crypto_digest_t will be created from a pool. If
+     *       *d is not NULL, *d must point at a previously created structure.
+     * @param d The digest context returned, see note.
+     * @param key The key structure to use.
+     * @param rec The digest record.
+     * @param p The pool to use.
+     * @return APR_ENOIV if an initialisation vector is required but not specified.
+     * @return APR_EINIT if the backend failed to initialise the context.
+     * @return APR_ENOTIMPL if not implemented.
+     * @return APR_NOKEY if the key type does not support the given operation.
+     */
+    apr_status_t (*digest_init)(apr_crypto_digest_t **d,
+            const apr_crypto_key_t *key, apr_crypto_digest_rec_t *rec, apr_pool_t *p);
+
+    /**
+     * @brief Update the digest with data provided by in.
+     * @param in Address of the buffer to read.
+     * @param inlen Length of the buffer to read.
+     * @param digest The digest context to use.
+     * @return APR_ECRYPT if an error occurred.
+     * @return APR_ENOTIMPL if not implemented.
+     * @return APR_NOKEY if the key type does not support the given operation.
+     */
+    apr_status_t (*digest_update)(apr_crypto_digest_t *digest,
+            const unsigned char *in, apr_size_t inlen);
+
+    /**
+     * @brief Finalise the digest and write the result.
+     * @note After this call, the context is cleaned and can be reused by
+     *   apr_crypto_digest_init().
+     * @param digest The digest 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.
+     * @return APR_NOKEY if the key type does not support the given operation.
+     */
+    apr_status_t (*digest_final)(apr_crypto_digest_t *digest);
+
+    /**
+     * @brief One shot digest on a single memory buffer.
+     * @param key The key structure to use.
+     * @param rec The digest record.
+     * @param in Address of the buffer to digest.
+     * @param inlen Length of the buffer to digest.
+     * @param p The pool to use.
+     * @return APR_ENOIV if an initialisation vector is required but not specified.
+     * @return APR_EINIT if the backend failed to initialise the context.
+     * @return APR_ENOTIMPL if not implemented.
+     * @return APR_NOKEY if the key type does not support the given operation.
+     */
+    apr_status_t (*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);
+
     /**
      * @brief Clean encryption / decryption context.
      * @note After cleanup, a context is free to be reused if necessary.
@@ -244,6 +313,14 @@ struct apr_crypto_driver_t {
      */
     apr_status_t (*block_cleanup)(apr_crypto_block_t *ctx);
 
+    /**
+     * @brief Clean sign / verify context.
+     * @note After cleanup, a context is free to be reused if necessary.
+     * @param ctx The digest context to use.
+     * @return Returns APR_ENOTIMPL if not supported.
+     */
+    apr_status_t (*digest_cleanup)(apr_crypto_digest_t *ctx);
+
     /**
      * @brief Clean encryption / decryption context.
      * @note After cleanup, a context is free to be reused if necessary.
diff --git a/test/testcrypto.c b/test/testcrypto.c
index bfcb7366571..9261d1887cd 100644
--- a/test/testcrypto.c
+++ b/test/testcrypto.c
@@ -113,6 +113,117 @@ static apr_crypto_t *make(abts_case *tc, apr_pool_t *pool,
 
 }
 
+static const apr_crypto_key_t *keyhash(abts_case *tc, apr_pool_t *pool,
+        const apr_crypto_driver_t *driver, const apr_crypto_t *f,
+        apr_crypto_block_key_digest_e digest, const char *description)
+{
+    apr_crypto_key_t *key = NULL;
+    const apu_err_t *result = NULL;
+    apr_crypto_key_rec_t *rec = apr_crypto_key_rec_make(APR_CRYPTO_KTYPE_HASH,
+            pool);
+    apr_status_t rv;
+
+    if (!driver) {
+        return NULL;
+    }
+
+    if (!f) {
+        return NULL;
+    }
+
+    rec->k.hash.digest = digest;
+
+    /* init the key */
+    rv = apr_crypto_key(&key, rec, f, pool);
+    if (APR_ENOCIPHER == rv || APR_ENODIGEST == rv) {
+        apr_crypto_error(&result, f);
+        ABTS_NOT_IMPL(tc,
+                apr_psprintf(pool, "skipped: %s %s key return APR_ENOTIMPL: error %d: %s (%s)\n", description, apr_crypto_driver_name(driver), result->rc, result->reason ? result->reason : "", result->msg ? result->msg : ""));
+        return NULL;
+    }
+    else {
+        if (APR_SUCCESS != rv) {
+            apr_crypto_error(&result, f);
+            fprintf(stderr, "key: %s %s apr error %d / native error %d: %s (%s)\n",
+                    description, apr_crypto_driver_name(driver), rv, result->rc,
+                    result->reason ? result->reason : "",
+                    result->msg ? result->msg : "");
+        }
+        ABTS_ASSERT(tc, "apr_crypto_key returned APR_EKEYLENGTH", rv != APR_EKEYLENGTH);
+        ABTS_ASSERT(tc, "apr_crypto_key returned APR_ENOKEY", rv != APR_ENOKEY);
+        ABTS_ASSERT(tc, "apr_crypto_key returned APR_EPADDING",
+                rv != APR_EPADDING);
+        ABTS_ASSERT(tc, "apr_crypto_key returned APR_EKEYTYPE",
+                rv != APR_EKEYTYPE);
+        ABTS_ASSERT(tc, "failed to apr_crypto_key", rv == APR_SUCCESS);
+        ABTS_ASSERT(tc, "apr_crypto_key returned NULL context", key != NULL);
+    }
+    if (rv) {
+        return NULL;
+    }
+    return key;
+
+}
+
+static const apr_crypto_key_t *keyhmac(abts_case *tc, apr_pool_t *pool,
+        const apr_crypto_driver_t *driver, const apr_crypto_t *f,
+        apr_crypto_block_key_digest_e digest, apr_crypto_block_key_type_e type,
+        apr_crypto_block_key_mode_e mode, int doPad, apr_size_t secretLen,
+        const char *description)
+{
+    apr_crypto_key_t *key = NULL;
+    const apu_err_t *result = NULL;
+    apr_crypto_key_rec_t *rec = apr_crypto_key_rec_make(APR_CRYPTO_KTYPE_HMAC,
+            pool);
+    apr_status_t rv;
+
+    if (!driver) {
+        return NULL;
+    }
+
+    if (!f) {
+        return NULL;
+    }
+
+    rec->type = type;
+    rec->mode = mode;
+    rec->pad = doPad;
+    rec->k.hmac.digest = digest;
+    rec->k.hmac.secret = apr_pcalloc(pool, secretLen);
+    rec->k.hmac.secretLen = secretLen;
+
+    /* init the key */
+    rv = apr_crypto_key(&key, rec, f, pool);
+    if (APR_ENOCIPHER == rv || APR_ENODIGEST == rv) {
+        apr_crypto_error(&result, f);
+        ABTS_NOT_IMPL(tc,
+                apr_psprintf(pool, "skipped: %s %s key return APR_ENOTIMPL: error %d: %s (%s)\n", description, apr_crypto_driver_name(driver), result->rc, result->reason ? result->reason : "", result->msg ? result->msg : ""));
+        return NULL;
+    }
+    else {
+        if (APR_SUCCESS != rv) {
+            apr_crypto_error(&result, f);
+            fprintf(stderr, "key: %s %s apr error %d / native error %d: %s (%s)\n",
+                    description, apr_crypto_driver_name(driver), rv, result->rc,
+                    result->reason ? result->reason : "",
+                    result->msg ? result->msg : "");
+        }
+        ABTS_ASSERT(tc, "apr_crypto_key returned APR_EKEYLENGTH", rv != APR_EKEYLENGTH);
+        ABTS_ASSERT(tc, "apr_crypto_key returned APR_ENOKEY", rv != APR_ENOKEY);
+        ABTS_ASSERT(tc, "apr_crypto_key returned APR_EPADDING",
+                rv != APR_EPADDING);
+        ABTS_ASSERT(tc, "apr_crypto_key returned APR_EKEYTYPE",
+                rv != APR_EKEYTYPE);
+        ABTS_ASSERT(tc, "failed to apr_crypto_key", rv == APR_SUCCESS);
+        ABTS_ASSERT(tc, "apr_crypto_key returned NULL context", key != NULL);
+    }
+    if (rv) {
+        return NULL;
+    }
+    return key;
+
+}
+
 static const apr_crypto_key_t *keysecret(abts_case *tc, apr_pool_t *pool,
         const apr_crypto_driver_t *driver, const apr_crypto_t *f,
         apr_crypto_block_key_type_e type, apr_crypto_block_key_mode_e mode,
@@ -120,14 +231,18 @@ static const apr_crypto_key_t *keysecret(abts_case *tc, apr_pool_t *pool,
 {
     apr_crypto_key_t *key = NULL;
     const apu_err_t *result = NULL;
-    apr_crypto_key_rec_t *rec = apr_pcalloc(pool, sizeof(apr_crypto_key_rec_t));
+    apr_crypto_key_rec_t *rec = apr_crypto_key_rec_make(APR_CRYPTO_KTYPE_SECRET,
+            pool);
     apr_status_t rv;
 
+    if (!driver) {
+        return NULL;
+    }
+
     if (!f) {
         return NULL;
     }
 
-    rec->ktype = APR_CRYPTO_KTYPE_SECRET;
     rec->type = type;
     rec->mode = mode;
     rec->pad = doPad;
@@ -178,6 +293,10 @@ static const apr_crypto_key_t *passphrase(abts_case *tc, apr_pool_t *pool,
     const char *salt = "salt";
     apr_status_t rv;
 
+    if (!driver) {
+        return NULL;
+    }
+
     if (!f) {
         return NULL;
     }
@@ -228,6 +347,10 @@ static const apr_crypto_key_t *keypassphrase(abts_case *tc, apr_pool_t *pool,
     apr_crypto_key_rec_t *rec = apr_pcalloc(pool, sizeof(apr_crypto_key_rec_t));
     apr_status_t rv;
 
+    if (!driver) {
+        return NULL;
+    }
+
     if (!f) {
         return NULL;
     }
@@ -450,6 +573,275 @@ static unsigned char *decrypt_block(abts_case *tc, apr_pool_t *pool,
 
 }
 
+static apr_status_t sign_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 **signature,
+        apr_size_t *signatureLen,
+        apr_size_t *blockSize, const char *description)
+{
+
+    apr_crypto_digest_t *digest = NULL;
+    const apu_err_t *result = NULL;
+    apr_crypto_digest_rec_t *rec = apr_crypto_digest_rec_make(
+            APR_CRYPTO_DTYPE_SIGN, pool);
+    apr_status_t rv;
+
+    if (!driver || !f || !key || !in) {
+        return APR_EGENERAL;
+    }
+
+    /* init the signature */
+    rv = apr_crypto_digest_init(&digest, key, rec, pool);
+    if (APR_ENOTIMPL == rv) {
+        ABTS_NOT_IMPL(tc, "apr_crypto_digest_init returned APR_ENOTIMPL");
+    }
+    else {
+        if (APR_SUCCESS != rv) {
+            apr_crypto_error(&result, f);
+            fprintf(stderr,
+                    "sign_init: %s %s (APR %d) native error %d: %s (%s)\n",
+                    description, apr_crypto_driver_name(driver), rv, result->rc,
+                    result->reason ? result->reason : "",
+                    result->msg ? result->msg : "");
+        }
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_ENOKEY",
+                rv != APR_ENOKEY);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_ENOIV",
+                rv != APR_ENOIV);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_EKEYTYPE",
+                rv != APR_EKEYTYPE);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_EKEYLENGTH",
+                rv != APR_EKEYLENGTH);
+        ABTS_ASSERT(tc,
+                "apr_crypto_digest_init returned APR_ENOTENOUGHENTROPY",
+                rv != APR_ENOTENOUGHENTROPY);
+        ABTS_ASSERT(tc, "failed to apr_crypto_digest_init",
+                rv == APR_SUCCESS);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned NULL context",
+                digest != NULL);
+    }
+    if (!digest || rv) {
+        return rv;
+    }
+
+    /* sign the block */
+    rv = apr_crypto_digest_update(digest, in, inlen);
+    if (APR_SUCCESS != rv) {
+        apr_crypto_error(&result, f);
+        fprintf(stderr, "sign: %s %s (APR %d) native error %d: %s (%s)\n",
+                description, apr_crypto_driver_name(driver), rv, result->rc,
+                result->reason ? result->reason : "",
+                result->msg ? result->msg : "");
+    }
+    ABTS_ASSERT(tc, "apr_crypto_digest returned APR_ECRYPT", rv != APR_ECRYPT);
+    ABTS_ASSERT(tc, "failed to apr_crypto_digest", rv == APR_SUCCESS);
+    if (rv) {
+        return rv;
+    }
+
+    /* finalise the sign */
+    rv = apr_crypto_digest_final(digest);
+    if (APR_SUCCESS != rv) {
+        apr_crypto_error(&result, f);
+        fprintf(stderr,
+                "sign_finish: %s %s (APR %d) native error %d: %s (%s)\n",
+                description, apr_crypto_driver_name(driver), rv, result->rc,
+                result->reason ? result->reason : "",
+                result->msg ? result->msg : "");
+    }
+    ABTS_ASSERT(tc, "apr_crypto_digest_final returned APR_ECRYPT", rv != APR_ECRYPT);
+    ABTS_ASSERT(tc, "apr_crypto_digest_final returned APR_EPADDING", rv != APR_EPADDING);
+    ABTS_ASSERT(tc, "apr_crypto_digest_final returned APR_ENOSPACE", rv != APR_ENOSPACE);
+    ABTS_ASSERT(tc, "failed to apr_crypto_digest_final", rv == APR_SUCCESS);
+    ABTS_ASSERT(tc, "apr_crypto_digest_final failed to allocate buffer", rec->d.sign.s != NULL);
+
+    apr_crypto_digest_cleanup(digest);
+
+    *signature = rec->d.sign.s;
+    *signatureLen = rec->d.sign.slen;
+
+    return rv;
+
+}
+
+static apr_status_t hash_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 **hash,
+        apr_size_t *hashLen,
+        apr_size_t *blockSize, const char *description)
+{
+
+    apr_crypto_digest_t *digest = NULL;
+    const apu_err_t *result = NULL;
+    apr_crypto_digest_rec_t *rec = apr_crypto_digest_rec_make(
+            APR_CRYPTO_DTYPE_HASH, pool);
+    apr_status_t rv;
+
+    if (!driver || !f || !key || !in) {
+        return APR_EGENERAL;
+    }
+
+    rec->d.hash.digest = APR_CRYPTO_DIGEST_SHA256;
+
+    /* init the signature */
+    rv = apr_crypto_digest_init(&digest, key, rec, pool);
+    if (APR_ENOTIMPL == rv) {
+        ABTS_NOT_IMPL(tc, "apr_crypto_digest_init returned APR_ENOTIMPL");
+    }
+    else {
+        if (APR_SUCCESS != rv) {
+            apr_crypto_error(&result, f);
+            fprintf(stderr,
+                    "sign_init: %s %s (APR %d) native error %d: %s (%s)\n",
+                    description, apr_crypto_driver_name(driver), rv, result->rc,
+                    result->reason ? result->reason : "",
+                    result->msg ? result->msg : "");
+        }
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_ENOKEY",
+                rv != APR_ENOKEY);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_ENOIV",
+                rv != APR_ENOIV);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_EKEYTYPE",
+                rv != APR_EKEYTYPE);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_EKEYLENGTH",
+                rv != APR_EKEYLENGTH);
+        ABTS_ASSERT(tc,
+                "apr_crypto_digest_init returned APR_ENOTENOUGHENTROPY",
+                rv != APR_ENOTENOUGHENTROPY);
+        ABTS_ASSERT(tc, "failed to apr_crypto_digest_init",
+                rv == APR_SUCCESS);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned NULL context",
+                digest != NULL);
+    }
+    if (!digest || rv) {
+        return rv;
+    }
+
+    /* sign the block */
+    rv = apr_crypto_digest_update(digest, in, inlen);
+    if (APR_SUCCESS != rv) {
+        apr_crypto_error(&result, f);
+        fprintf(stderr, "sign: %s %s (APR %d) native error %d: %s (%s)\n",
+                description, apr_crypto_driver_name(driver), rv, result->rc,
+                result->reason ? result->reason : "",
+                result->msg ? result->msg : "");
+    }
+    ABTS_ASSERT(tc, "apr_crypto_digest returned APR_ECRYPT", rv != APR_ECRYPT);
+    ABTS_ASSERT(tc, "failed to apr_crypto_digest", rv == APR_SUCCESS);
+    if (rv) {
+        return rv;
+    }
+
+    /* finalise the sign */
+    rv = apr_crypto_digest_final(digest);
+    if (APR_SUCCESS != rv) {
+        apr_crypto_error(&result, f);
+        fprintf(stderr,
+                "sign_finish: %s %s (APR %d) native error %d: %s (%s)\n",
+                description, apr_crypto_driver_name(driver), rv, result->rc,
+                result->reason ? result->reason : "",
+                result->msg ? result->msg : "");
+    }
+    ABTS_ASSERT(tc, "apr_crypto_digest_final returned APR_ECRYPT", rv != APR_ECRYPT);
+    ABTS_ASSERT(tc, "apr_crypto_digest_final returned APR_EPADDING", rv != APR_EPADDING);
+    ABTS_ASSERT(tc, "apr_crypto_digest_final returned APR_ENOSPACE", rv != APR_ENOSPACE);
+    ABTS_ASSERT(tc, "failed to apr_crypto_digest_final", rv == APR_SUCCESS);
+    ABTS_ASSERT(tc, "apr_crypto_digest_final failed to allocate buffer", rec->d.hash.s != NULL);
+
+    apr_crypto_digest_cleanup(digest);
+
+    *hash = rec->d.hash.s;
+    *hashLen = rec->d.hash.slen;
+
+    return rv;
+
+}
+
+static apr_status_t verify_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,
+        apr_size_t inlen, const unsigned char *signature,
+        apr_size_t signatureLen,
+        apr_size_t *blockSize, const char *description)
+{
+
+    apr_crypto_digest_t *digest = NULL;
+    const apu_err_t *result = NULL;
+    apr_crypto_digest_rec_t *rec = apr_crypto_digest_rec_make(
+            APR_CRYPTO_DTYPE_VERIFY, pool);
+    apr_status_t rv;
+
+    if (!driver || !f || !key || !in || !signature) {
+        return APR_EGENERAL;
+    }
+
+    rec->d.verify.v = signature;
+    rec->d.verify.vlen = signatureLen;
+
+    /* init the decryption */
+    rv = apr_crypto_digest_init(&digest, key, rec, pool);
+    if (APR_ENOTIMPL == rv) {
+        ABTS_NOT_IMPL(tc, "apr_crypto_digest_init returned APR_ENOTIMPL");
+    }
+    else {
+        if (APR_SUCCESS != rv) {
+            apr_crypto_error(&result, f);
+            fprintf(stderr,
+                    "digest_init: %s %s (APR %d) native error %d: %s (%s)\n",
+                    description, apr_crypto_driver_name(driver), rv, result->rc,
+                    result->reason ? result->reason : "",
+                    result->msg ? result->msg : "");
+        }
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_ENOKEY", rv != APR_ENOKEY);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_ENOIV", rv != APR_ENOIV);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_EKEYTYPE", rv != APR_EKEYTYPE);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned APR_EKEYLENGTH", rv != APR_EKEYLENGTH);
+        ABTS_ASSERT(tc, "failed to apr_crypto_digest_init", rv == APR_SUCCESS);
+        ABTS_ASSERT(tc, "apr_crypto_digest_init returned NULL context", digest != NULL);
+    }
+    if (!digest || rv) {
+        return APR_EGENERAL;
+    }
+
+    /* decrypt the block */
+    rv = apr_crypto_digest_update(digest, in, inlen);
+    if (APR_SUCCESS != rv) {
+        apr_crypto_error(&result, f);
+        fprintf(stderr, "decrypt: %s %s (APR %d) native error %d: %s (%s)\n",
+                description, apr_crypto_driver_name(driver), rv, result->rc,
+                result->reason ? result->reason : "",
+                result->msg ? result->msg : "");
+    }
+    ABTS_ASSERT(tc, "apr_crypto_digest returned APR_ECRYPT", rv != APR_ECRYPT);
+    ABTS_ASSERT(tc, "failed to apr_crypto_digest", rv == APR_SUCCESS);
+    ABTS_ASSERT(tc, "apr_crypto_digest failed to allocate buffer", signature != NULL);
+    if (rv) {
+        return APR_EGENERAL;
+    }
+
+    /* finalise the decryption */
+    rv = apr_crypto_digest_final(digest);
+    if (APR_SUCCESS != rv) {
+        apr_crypto_error(&result, f);
+        fprintf(stderr,
+                "verify_finish: %s %s (APR %d) native error %d: %s (%s)\n",
+                description, apr_crypto_driver_name(driver), rv, result->rc,
+                result->reason ? result->reason : "",
+                result->msg ? result->msg : "");
+    }
+    ABTS_ASSERT(tc, "apr_crypto_digest_final returned APR_ECRYPT", rv != APR_ECRYPT);
+    ABTS_ASSERT(tc, "apr_crypto_digest_final returned APR_EPADDING", rv != APR_EPADDING);
+    ABTS_ASSERT(tc, "apr_crypto_digest_final returned APR_ENOSPACE", rv != APR_ENOSPACE);
+    ABTS_ASSERT(tc, "failed to apr_crypto_digest_final", rv == APR_SUCCESS);
+
+    apr_crypto_digest_cleanup(digest);
+
+    return rv;
+
+}
+
 /**
  * Interoperability test.
  *
@@ -549,6 +941,130 @@ static void crypto_block_cross(abts_case *tc, apr_pool_t *pool,
 
 }
 
+/**
+ * Interoperability test.
+ *
+ * data must point at an array of two driver structures. Data will be signed
+ * with the first driver, and verified with the second.
+ *
+ * If the two drivers interoperate, the test passes.
+ */
+static void crypto_cross_hash(abts_case *tc, apr_pool_t *pool,
+        const apr_crypto_driver_t **drivers,
+        const apr_crypto_block_key_digest_e digest,
+        const unsigned char *in, apr_size_t inlen,
+        const char *description)
+{
+    const apr_crypto_driver_t *driver1 = drivers[0];
+    const apr_crypto_driver_t *driver2 = drivers[1];
+    apr_crypto_t *f1 = NULL;
+    apr_crypto_t *f2 = NULL;
+    const apr_crypto_key_t *key7 = NULL;
+    const apr_crypto_key_t *key8 = NULL;
+
+    apr_size_t blockSize = 0;
+    unsigned char *hash1 = NULL;
+    unsigned char *hash2 = NULL;
+    apr_size_t hash1Len = 0;
+    apr_size_t hash2Len = 0;
+
+    apr_status_t rv;
+
+    f1 = make(tc, pool, driver1);
+    f2 = make(tc, pool, driver2);
+
+    key7 = keyhash(tc, pool, driver1, f1, digest, description);
+    key8 = keyhash(tc, pool, driver2, f2, digest, description);
+
+    blockSize = 0;
+    rv = hash_block(tc, pool, driver1, f1, key7, in, inlen,
+            &hash1, &hash1Len, &blockSize, description);
+
+    if (APR_SUCCESS != rv && driver1 && driver2) {
+        fprintf(stderr, "key hash cross error %d: %s %s/%s\n", rv, description,
+                apr_crypto_driver_name(driver1),
+                apr_crypto_driver_name(driver2));
+    }
+
+    rv = hash_block(tc, pool, driver2, f2, key8, in,
+            inlen, &hash2, &hash2Len, &blockSize,
+            description);
+
+    if (APR_SUCCESS != rv && driver1 && driver2) {
+        fprintf(stderr, "key hash cross error %d: %s %s/%s\n", rv, description,
+                apr_crypto_driver_name(driver1),
+                apr_crypto_driver_name(driver2));
+    }
+
+    if (driver1 && driver2
+            && (!hash1 || !hash2 || hash1Len != hash2Len
+                    || memcmp(hash1, hash2, hash1Len))) {
+        fprintf(stderr, "key hash cross mismatch (hash): %s %s/%s\n", description,
+                apr_crypto_driver_name(driver1),
+                apr_crypto_driver_name(driver2));
+    }
+
+}
+
+/**
+ * Interoperability test.
+ *
+ * data must point at an array of two driver structures. Data will be signed
+ * with the first driver, and verified with the second.
+ *
+ * If the two drivers interoperate, the test passes.
+ */
+static void crypto_cross_sign(abts_case *tc, apr_pool_t *pool,
+        const apr_crypto_driver_t **drivers,
+        const apr_crypto_block_key_digest_e digest,
+        const apr_crypto_block_key_type_e type,
+        const apr_crypto_block_key_mode_e mode, int doPad,
+        const unsigned char *in, apr_size_t inlen, apr_size_t secretLen,
+        const char *description)
+{
+    const apr_crypto_driver_t *driver1 = drivers[0];
+    const apr_crypto_driver_t *driver2 = drivers[1];
+    apr_crypto_t *f1 = NULL;
+    apr_crypto_t *f2 = NULL;
+    const apr_crypto_key_t *key7 = NULL;
+    const apr_crypto_key_t *key8 = NULL;
+
+    apr_size_t blockSize = 0;
+    unsigned char *signature = NULL;
+    apr_size_t signatureLen = 0;
+
+    apr_status_t rv;
+
+    f1 = make(tc, pool, driver1);
+    f2 = make(tc, pool, driver2);
+
+    key7 = keyhmac(tc, pool, driver1, f1, digest, type, mode, doPad, secretLen,
+            description);
+    key8 = keyhmac(tc, pool, driver2, f2, digest, type, mode, doPad, secretLen,
+            description);
+
+    blockSize = 0;
+    rv = sign_block(tc, pool, driver1, f1, key7, in, inlen,
+            &signature, &signatureLen, &blockSize, description);
+
+    if (APR_SUCCESS != rv && driver1 && driver2) {
+        fprintf(stderr, "key hmac cross mismatch (sign): %s %s/%s\n", description,
+                apr_crypto_driver_name(driver1),
+                apr_crypto_driver_name(driver2));
+    }
+
+    rv = verify_block(tc, pool, driver2, f2, key8, in,
+            inlen, signature, signatureLen, &blockSize,
+            description);
+
+    if (APR_SUCCESS != rv && driver1 && driver2) {
+        fprintf(stderr, "key hmac cross mismatch (verify): %s %s/%s\n", description,
+                apr_crypto_driver_name(driver1),
+                apr_crypto_driver_name(driver2));
+    }
+
+}
+
 /**
  * Test initialisation.
  */
@@ -559,10 +1075,7 @@ static void test_crypto_init(abts_case *tc, void *data)
 
     apr_pool_create(&pool, NULL);
 
-    /* Use root pool (top level init) so that the crypto lib is
-     * not cleanup on pool destroy below (e.g. openssl can't re-init).
-     */
-    rv = apr_crypto_init(apr_pool_parent_get(pool));
+    rv = apr_crypto_init(pool);
     ABTS_ASSERT(tc, "failed to init apr_crypto", rv == APR_SUCCESS);
 
     apr_pool_destroy(pool);
@@ -660,6 +1173,62 @@ static void test_crypto_block_openssl(abts_case *tc, void *data)
 
 }
 
+/**
+ * Simple test of OpenSSL block signatures.
+ */
+static void test_crypto_digest_openssl(abts_case *tc, void *data)
+{
+    apr_pool_t *pool = NULL;
+    const apr_crypto_driver_t *drivers[] = { NULL, NULL };
+
+    const unsigned char *in = (const unsigned char *) ALIGNED_STRING;
+    apr_size_t inlen = sizeof(ALIGNED_STRING);
+
+    apr_pool_create(&pool, NULL);
+    drivers[0] = get_openssl_driver(tc, pool);
+    drivers[1] = get_openssl_driver(tc, pool);
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_CBC, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_ECB, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_CBC, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_ECB, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_ECB");
+
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_MD5, in, inlen,
+            "DIGEST MD5");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA1, in, inlen,
+            "DIGEST SHA1");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA224, in, inlen,
+            "DIGEST SHA224");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256, in, inlen,
+            "DIGEST SHA256");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA384, in, inlen,
+            "DIGEST SHA384");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA512, in, inlen,
+            "DIGEST SHA512");
+
+    apr_pool_destroy(pool);
+
+}
+
 /**
  * Simple test of NSS block crypt.
  */
@@ -694,6 +1263,61 @@ static void test_crypto_block_nss(abts_case *tc, void *data)
 
 }
 
+/**
+ * Simple test of NSS block sign/verify.
+ */
+static void test_crypto_digest_nss(abts_case *tc, void *data)
+{
+    apr_pool_t *pool = NULL;
+    const apr_crypto_driver_t *drivers[] = { NULL, NULL };
+
+    const unsigned char *in = (const unsigned char *) ALIGNED_STRING;
+    apr_size_t inlen = sizeof(ALIGNED_STRING);
+
+    apr_pool_create(&pool, NULL);
+    drivers[0] = get_nss_driver(tc, pool);
+    drivers[1] = get_nss_driver(tc, pool);
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_CBC");
+    /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+    /* crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256, KEY_3DES_192, MODE_ECB, 0, in, inlen, "KEY_3DES_192/MODE_ECB"); */
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_CBC, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_ECB, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_CBC, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_ECB, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_ECB");
+
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_MD5, in, inlen,
+            "DIGEST MD5");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA1, in, inlen,
+            "DIGEST SHA1");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA224, in, inlen,
+            "DIGEST SHA224");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256, in, inlen,
+            "DIGEST SHA256");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA384, in, inlen,
+            "DIGEST SHA384");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA512, in, inlen,
+            "DIGEST SHA512");
+
+    apr_pool_destroy(pool);
+
+}
+
 /**
  * Simple test of Common Crypto block crypt.
  */
@@ -728,6 +1352,62 @@ static void test_crypto_block_commoncrypto(abts_case *tc, void *data)
 
 }
 
+/**
+ * Simple test of Common Crypto block sign.
+ */
+static void test_crypto_digest_commoncrypto(abts_case *tc, void *data)
+{
+    apr_pool_t *pool = NULL;
+    const apr_crypto_driver_t *drivers[] = { NULL, NULL };
+
+    const unsigned char *in = (const unsigned char *) ALIGNED_STRING;
+    apr_size_t inlen = sizeof(ALIGNED_STRING);
+
+    apr_pool_create(&pool, NULL);
+    drivers[0] = get_commoncrypto_driver(tc, pool);
+    drivers[1] = get_commoncrypto_driver(tc, pool);
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_CBC, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_ECB, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_CBC, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_ECB, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_ECB");
+
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_MD5, in, inlen,
+            "DIGEST MD5");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA1, in, inlen,
+            "DIGEST SHA1");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA224, in, inlen,
+            "DIGEST SHA224");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256, in, inlen,
+            "DIGEST SHA256");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA384, in, inlen,
+            "DIGEST SHA384");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA512, in, inlen,
+            "DIGEST SHA512");
+
+    apr_pool_destroy(pool);
+
+}
+
 /**
  * Encrypt NSS, decrypt OpenSSL.
  */
@@ -800,6 +1480,120 @@ static void test_crypto_block_openssl_nss(abts_case *tc, void *data)
 
 }
 
+/**
+ * Sign NSS, verify OpenSSL.
+ */
+static void test_crypto_digest_nss_openssl(abts_case *tc, void *data)
+{
+    apr_pool_t *pool = NULL;
+    const apr_crypto_driver_t *drivers[] = { NULL, NULL };
+
+    const unsigned char *in = (const unsigned char *) ALIGNED_STRING;
+    apr_size_t inlen = sizeof(ALIGNED_STRING);
+
+    apr_pool_create(&pool, NULL);
+    drivers[0] = get_nss_driver(tc, pool);
+    drivers[1] = get_openssl_driver(tc, pool);
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_CBC");
+    /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+/*    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_ECB");*/
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_CBC, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_ECB, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_CBC, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_ECB, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_ECB");
+
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_MD5, in, inlen,
+            "DIGEST MD5");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA1, in, inlen,
+            "DIGEST SHA1");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA224, in, inlen,
+            "DIGEST SHA224");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256, in, inlen,
+            "DIGEST SHA256");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA384, in, inlen,
+            "DIGEST SHA384");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA512, in, inlen,
+            "DIGEST SHA512");
+
+    apr_pool_destroy(pool);
+
+}
+
+/**
+ * Sign OpenSSL, verify NSS.
+ */
+static void test_crypto_digest_openssl_nss(abts_case *tc, void *data)
+{
+    apr_pool_t *pool = NULL;
+    const apr_crypto_driver_t *drivers[] = { NULL, NULL };
+
+    const unsigned char *in = (const unsigned char *) ALIGNED_STRING;
+    apr_size_t inlen = sizeof(ALIGNED_STRING);
+
+    apr_pool_create(&pool, NULL);
+    drivers[0] = get_openssl_driver(tc, pool);
+    drivers[1] = get_nss_driver(tc, pool);
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_CBC");
+    /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+/*    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_ECB");*/
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_CBC, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_ECB, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_CBC, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_ECB, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_ECB");
+
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_MD5, in, inlen,
+            "DIGEST MD5");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA1, in, inlen,
+            "DIGEST SHA1");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA224, in, inlen,
+            "DIGEST SHA224");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256, in, inlen,
+            "DIGEST SHA256");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA384, in, inlen,
+            "DIGEST SHA384");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA512, in, inlen,
+            "DIGEST SHA512");
+
+    apr_pool_destroy(pool);
+
+}
+
 /**
  * Encrypt OpenSSL, decrypt CommonCrypto.
  */
@@ -836,6 +1630,63 @@ static void test_crypto_block_openssl_commoncrypto(abts_case *tc, void *data)
 
 }
 
+/**
+ * Sign OpenSSL, verify CommonCrypto.
+ */
+static void test_crypto_digest_openssl_commoncrypto(abts_case *tc, void *data)
+{
+    apr_pool_t *pool = NULL;
+    const apr_crypto_driver_t *drivers[] = { NULL, NULL };
+
+    const unsigned char *in = (const unsigned char *) ALIGNED_STRING;
+    apr_size_t inlen = sizeof(ALIGNED_STRING);
+
+    apr_pool_create(&pool, NULL);
+    drivers[0] = get_openssl_driver(tc, pool);
+    drivers[1] = get_commoncrypto_driver(tc, pool);
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_CBC");
+    /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+/*    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_ECB");*/
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_CBC, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_ECB, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_CBC, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_ECB, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_ECB");
+
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_MD5, in, inlen,
+            "DIGEST MD5");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA1, in, inlen,
+            "DIGEST SHA1");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA224, in, inlen,
+            "DIGEST SHA224");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256, in, inlen,
+            "DIGEST SHA256");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA384, in, inlen,
+            "DIGEST SHA384");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA512, in, inlen,
+            "DIGEST SHA512");
+
+    apr_pool_destroy(pool);
+
+}
+
 /**
  * Encrypt OpenSSL, decrypt CommonCrypto.
  */
@@ -872,6 +1723,63 @@ static void test_crypto_block_commoncrypto_openssl(abts_case *tc, void *data)
 
 }
 
+/**
+ * Sign OpenSSL, verify CommonCrypto.
+ */
+static void test_crypto_digest_commoncrypto_openssl(abts_case *tc, void *data)
+{
+    apr_pool_t *pool = NULL;
+    const apr_crypto_driver_t *drivers[] = { NULL, NULL };
+
+    const unsigned char *in = (const unsigned char *) ALIGNED_STRING;
+    apr_size_t inlen = sizeof(ALIGNED_STRING);
+
+    apr_pool_create(&pool, NULL);
+    drivers[0] = get_commoncrypto_driver(tc, pool);
+    drivers[1] = get_openssl_driver(tc, pool);
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_CBC");
+    /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+/*    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_3DES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_3DES_192/MODE_ECB");*/
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_CBC, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_256, APR_MODE_ECB, 0, in, inlen, 32,
+            "KEY_AES_256/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_CBC, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_192, APR_MODE_ECB, 0, in, inlen, 24,
+            "KEY_AES_192/MODE_ECB");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_CBC, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_CBC");
+    crypto_cross_sign(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256,
+            APR_KEY_AES_128, APR_MODE_ECB, 0, in, inlen, 16,
+            "KEY_AES_128/MODE_ECB");
+
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_MD5, in, inlen,
+            "DIGEST MD5");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA1, in, inlen,
+            "DIGEST SHA1");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA224, in, inlen,
+            "DIGEST SHA224");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA256, in, inlen,
+            "DIGEST SHA256");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA384, in, inlen,
+            "DIGEST SHA384");
+    crypto_cross_hash(tc, pool, drivers, APR_CRYPTO_DIGEST_SHA512, in, inlen,
+            "DIGEST SHA512");
+
+    apr_pool_destroy(pool);
+
+}
+
 /**
  * Simple test of OpenSSL block crypt.
  */
@@ -1699,7 +2607,7 @@ abts_suite *testcrypto(abts_suite *suite)
 {
     suite = ADD_SUITE(suite);
 
-    /* test simple init and shutdown (keep first) */
+    /* test simple init and shutdown */
     abts_run_test(suite, test_crypto_init, NULL);
 
     /* test key parsing - openssl */
@@ -1714,45 +2622,71 @@ abts_suite *testcrypto(abts_suite *suite)
     /* test a simple encrypt / decrypt operation - openssl */
     abts_run_test(suite, test_crypto_block_openssl, NULL);
 
+    /* test a simple sign / verify operation - openssl */
+    abts_run_test(suite, test_crypto_digest_openssl, NULL);
+
     /* test a padded encrypt / decrypt operation - openssl */
     abts_run_test(suite, test_crypto_block_openssl_pad, NULL);
 
     /* test a simple encrypt / decrypt operation - nss */
     abts_run_test(suite, test_crypto_block_nss, NULL);
 
+    /* test a simple sign / verify operation - nss */
+    abts_run_test(suite, test_crypto_digest_nss, NULL);
+
     /* test a padded encrypt / decrypt operation - nss */
     abts_run_test(suite, test_crypto_block_nss_pad, NULL);
 
     /* test a simple encrypt / decrypt operation - commoncrypto */
     abts_run_test(suite, test_crypto_block_commoncrypto, NULL);
 
+    /* test a simple sign / verify operation - commoncrypto */
+    abts_run_test(suite, test_crypto_digest_commoncrypto, NULL);
+
     /* test a padded encrypt / decrypt operation - commoncrypto */
     abts_run_test(suite, test_crypto_block_commoncrypto_pad, NULL);
 
+
     /* test encrypt nss / decrypt openssl */
     abts_run_test(suite, test_crypto_block_nss_openssl, NULL);
 
     /* test padded encrypt nss / decrypt openssl */
     abts_run_test(suite, test_crypto_block_nss_openssl_pad, NULL);
 
+    /* test sign nss / verify openssl */
+    abts_run_test(suite, test_crypto_digest_nss_openssl, NULL);
+
+
     /* test encrypt openssl / decrypt nss */
     abts_run_test(suite, test_crypto_block_openssl_nss, NULL);
 
     /* test padded encrypt openssl / decrypt nss */
     abts_run_test(suite, test_crypto_block_openssl_nss_pad, NULL);
 
+    /* test sign openssl / verify nss */
+    abts_run_test(suite, test_crypto_digest_openssl_nss, NULL);
+
+
     /* test encrypt openssl / decrypt commoncrypto */
     abts_run_test(suite, test_crypto_block_openssl_commoncrypto, NULL);
 
     /* test padded encrypt openssl / decrypt commoncrypto */
     abts_run_test(suite, test_crypto_block_openssl_commoncrypto_pad, NULL);
 
+    /* test sign openssl / verify commoncrypto */
+    abts_run_test(suite, test_crypto_digest_openssl_commoncrypto, NULL);
+
+
     /* test encrypt commoncrypto / decrypt openssl */
     abts_run_test(suite, test_crypto_block_commoncrypto_openssl, NULL);
 
     /* test padded encrypt commoncrypto / decrypt openssl */
     abts_run_test(suite, test_crypto_block_commoncrypto_openssl_pad, NULL);
 
+    /* test sign commoncrypto / verify openssl */
+    abts_run_test(suite, test_crypto_digest_commoncrypto_openssl, NULL);
+
+
     /* test block key types openssl */
     abts_run_test(suite, test_crypto_get_block_key_types_openssl, NULL);