summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorminfrin <minfrin@13f79535-47bb-0310-9956-ffa450edef68>2008-09-07 20:42:47 +0000
committerminfrin <minfrin@13f79535-47bb-0310-9956-ffa450edef68>2008-09-07 20:42:47 +0000
commit73d8835d9c69243fdf27d7231420ad6f154c74df (patch)
tree6f6afad335f6b7f31eca03108b38ab085d2e5f8e
parent69fb31e9a20ae26dbb54bdd6818b7f6b9df29283 (diff)
downloadlibapr-util-73d8835d9c69243fdf27d7231420ad6f154c74df.tar.gz
Add apr_crypto implementations for OpenSSL and Mozilla NSS. Add a unit
test to verify the interoperability of the two modules. Builds default to disabled unless explicitly enabled. git-svn-id: http://svn.apache.org/repos/asf/apr/apr-util/trunk@692949 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--CHANGES5
-rw-r--r--Makefile.in2
-rw-r--r--build.conf17
-rw-r--r--build/crypto.m4205
-rw-r--r--build/dso.m48
-rw-r--r--configure.in4
-rw-r--r--crypto/apr_crypto_nss.c766
-rw-r--r--crypto/apr_crypto_openssl.c649
-rw-r--r--test/Makefile.in6
-rw-r--r--test/abts_tests.h1
-rw-r--r--test/testcrypto.c698
-rw-r--r--test/testutil.h1
12 files changed, 2356 insertions, 6 deletions
diff --git a/CHANGES b/CHANGES
index 682d537b..8cc032f1 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,11 @@
-*- coding: utf-8 -*-
Changes with APR-util 1.4.0
+ *) Add apr_crypto implementations for OpenSSL and Mozilla NSS. Add a unit
+ test to verify the interoperability of the two modules. Builds default
+ to disabled unless explicitly enabled.
+ [Graham Leggett]
+
*) Add the apr_crypto interface, a rewrite of the earlier apr_ssl code,
based on the modular dso interface used for dbd and ldap. Initially,
the interface supports symmetrical encryption and decryption. The
diff --git a/Makefile.in b/Makefile.in
index 9f78a92b..305d3302 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -38,6 +38,8 @@ LDADD_dbd_mysql = @LDADD_dbd_mysql@
LDADD_dbd_freetds = @LDADD_dbd_freetds@
LDADD_dbd_odbc = @LDADD_dbd_odbc@
LDADD_ldap = @LDADD_ldap@
+LDADD_crypto_openssl = @LDADD_crypto_openssl@
+LDADD_crypto_nss = @LDADD_crypto_nss@
TARGETS = $(TARGET_LIB) aprutil.exp apu-config.out $(APU_MODULES)
diff --git a/build.conf b/build.conf
index 27a12ec7..c7cb90a1 100644
--- a/build.conf
+++ b/build.conf
@@ -7,7 +7,12 @@
# the platform-independent .c files
paths =
buckets/*.c
- crypto/*.c
+ crypto/apr_crypto.c
+ crypto/apr_md4.c
+ crypto/apr_md5.c
+ crypto/apr_sha1.c
+ crypto/getuuid.c
+ crypto/uuid.c
dbm/*.c
dbm/sdbm/*.c
encoding/*.c
@@ -28,13 +33,21 @@ platform_dirs =
# the public headers
headers = include/*.h include/private/*.h
-modules = ldap dbd_pgsql dbd_sqlite2 dbd_sqlite3 dbd_oracle dbd_mysql dbd_freetds dbd_odbc
+modules = crypto_openssl crypto_nss ldap dbd_pgsql dbd_sqlite2 dbd_sqlite3 dbd_oracle dbd_mysql dbd_freetds dbd_odbc
# gen_uri_delim.c
# we have a recursive makefile for the test files (for now)
# test/*.c
+[crypto_openssl]
+paths = crypto/apr_crypto_openssl.c
+target = crypto/apr_crypto_openssl.la
+
+[crypto_nss]
+paths = crypto/apr_crypto_nss.c
+target = crypto/apr_crypto_nss.la
+
[dbd_pgsql]
paths = dbd/apr_dbd_pgsql.c
target = dbd/apr_dbd_pgsql.la
diff --git a/build/crypto.m4 b/build/crypto.m4
new file mode 100644
index 00000000..18725185
--- /dev/null
+++ b/build/crypto.m4
@@ -0,0 +1,205 @@
+dnl -------------------------------------------------------- -*- autoconf -*-
+dnl Copyright 2006 The Apache Software Foundation or its licensors, as
+dnl applicable.
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+
+dnl
+dnl Crypto module
+dnl
+
+dnl
+dnl APU_CHECK_CRYPTO: look for crypto libraries and headers
+dnl
+AC_DEFUN([APU_CHECK_CRYPTO], [
+ apu_have_crypto=0
+
+ old_libs="$LIBS"
+ old_cppflags="$CPPFLAGS"
+ old_ldflags="$LDFLAGS"
+
+ AC_ARG_WITH([crypto], [APR_HELP_STRING([--with-crypto], [enable crypto support])],
+ [
+ if test "$withval" = "yes"; then
+ APU_CHECK_CRYPTO_OPENSSL
+ APU_CHECK_CRYPTO_NSS
+ dnl add checks for other varieties of ssl here
+ fi
+ ], [
+ apu_have_crypto=0
+ ])
+
+ if test "$apu_have_crypto" = "1"; then
+ AC_DEFINE([APU_HAVE_CRYPTO], 1, [Define that we have crypto capability])
+ fi
+
+])
+dnl
+
+AC_DEFUN([APU_CHECK_CRYPTO_OPENSSL], [
+ apu_have_openssl=0
+ openssl_have_headers=0
+ openssl_have_libs=0
+
+ AC_ARG_WITH([openssl],
+ [APR_HELP_STRING([--with-openssl=DIR], [specify location of OpenSSL])],
+ [
+ if test "$withval" = "yes"; then
+ AC_CHECK_HEADERS(openssl/x509.h, [openssl_have_headers=1])
+ AC_CHECK_LIB(crypto, BN_init, AC_CHECK_LIB(ssl, SSL_accept, [openssl_have_libs=1],,-lcrypto))
+ if test "$openssl_have_headers" != "0" && test "$openssl_have_libs" != "0"; then
+ apu_have_openssl=1
+ fi
+ elif test "$withval" = "no"; then
+ apu_have_openssl=0
+ else
+ old_cppflags="$CPPFLAGS"
+ old_ldflags="$LDFLAGS"
+
+ openssl_CPPFLAGS="-I$withval/include"
+ openssl_LDFLAGS="-L$withval/lib "
+
+ APR_ADDTO(CPPFLAGS, [$openssl_CPPFLAGS])
+ APR_ADDTO(LDFLAGS, [$openssl_LDFLAGS])
+
+ AC_MSG_NOTICE(checking for openssl in $withval)
+ AC_CHECK_HEADERS(openssl/x509.h, [openssl_have_headers=1])
+ AC_CHECK_LIB(crypto, BN_init, AC_CHECK_LIB(ssl, SSL_accept, [openssl_have_libs=1],,-lcrypto))
+ if test "$openssl_have_headers" != "0" && test "$openssl_have_libs" != "0"; then
+ apu_have_openssl=1
+ APR_ADDTO(APRUTIL_LDFLAGS, [-L$withval/lib])
+ APR_ADDTO(APRUTIL_INCLUDES, [-I$withval/include])
+ fi
+
+ if test "$apu_have_openssl" != "1"; then
+ AC_CHECK_HEADERS(openssl/x509.h, [openssl_have_headers=1])
+ AC_CHECK_LIB(crypto, BN_init, AC_CHECK_LIB(ssl, SSL_accept, [openssl_have_libs=1],,-lcrypto))
+ if test "$openssl_have_headers" != "0" && test "$openssl_have_libs" != "0"; then
+ apu_have_openssl=1
+ APR_ADDTO(APRUTIL_LDFLAGS, [-L$withval/lib])
+ APR_ADDTO(APRUTIL_INCLUDES, [-I$withval/include])
+ fi
+ fi
+
+ AC_CHECK_DECLS([EVP_PKEY_CTX_new], [], [],
+ [#include <openssl/evp.h>])
+
+ CPPFLAGS="$old_cppflags"
+ LDFLAGS="$old_ldflags"
+ fi
+ ], [
+ apu_have_openssl=0
+ ])
+
+ AC_SUBST(apu_have_openssl)
+
+ dnl Since we have already done the AC_CHECK_LIB tests, if we have it,
+ dnl we know the library is there.
+ if test "$apu_have_openssl" = "1"; then
+ LDADD_crypto_openssl="$openssl_LDFLAGS -lssl -lcrypto"
+ apu_have_crypto=1
+ AC_DEFINE([APU_HAVE_CRYPTO], 1, [Define that we have crypto capability])
+
+ AC_MSG_CHECKING([for const input buffers in OpenSSL])
+ AC_TRY_COMPILE([#include <openssl/rsa.h>],
+ [ const unsigned char * buf;
+ unsigned char * outbuf;
+ RSA rsa;
+
+ RSA_private_decrypt(1,
+ buf,
+ outbuf,
+ &rsa,
+ RSA_PKCS1_PADDING);
+
+ ],
+ [AC_MSG_RESULT([yes])]
+ [AC_DEFINE([CRYPTO_OPENSSL_CONST_BUFFERS], 1, [Define that OpenSSL uses const buffers])],
+ [AC_MSG_RESULT([no])])
+
+ fi
+ AC_SUBST(LDADD_crypto_openssl)
+
+ LIBS="$old_libs"
+ CPPFLAGS="$old_cppflags"
+ LDFLAGS="$old_ldflags"
+])
+
+AC_DEFUN([APU_CHECK_CRYPTO_NSS], [
+ apu_have_nss=0
+ nss_have_headers=0
+ nss_have_libs=0
+
+ AC_ARG_WITH([nss],
+ [APR_HELP_STRING([--with-nss=DIR], [specify location of OpenSSL])],
+ [
+ if test "$withval" = "yes"; then
+ AC_CHECK_HEADERS(nspr/nspr.h nss/nss.h, [nss_have_headers=1])
+ AC_CHECK_LIB(nspr4, PR_Initialize, AC_CHECK_LIB(nss3, PK11_CreatePBEV2AlgorithmID, [nss_have_libs=1],,-lnspr4))
+ if test "$nss_have_headers" != "0" && test "$nss_have_libs" != "0"; then
+ apu_have_nss=1
+ fi
+ elif test "$withval" = "no"; then
+ apu_have_nss=0
+ elif test "x$withval" != "x"; then
+ old_cppflags="$CPPFLAGS"
+ old_ldflags="$LDFLAGS"
+
+ nss_CPPFLAGS="-I$withval/include -I$withval/../public"
+ nss_LDFLAGS="-L$withval/lib "
+
+ APR_ADDTO(CPPFLAGS, [$nss_CPPFLAGS])
+ APR_ADDTO(LDFLAGS, [$nss_LDFLAGS])
+
+ AC_MSG_NOTICE(checking for nss in $withval)
+ AC_CHECK_HEADERS(nspr/nspr.h nss/nss.h, [nss_have_headers=1])
+ AC_CHECK_LIB(nspr4, PR_Initialize, AC_CHECK_LIB(nss3, PK11_CreatePBEV2AlgorithmID, [nss_have_libs=1],,-lnspr4))
+ if test "$nss_have_headers" != "0" && test "$nss_have_libs" != "0"; then
+ apu_have_nss=1
+ APR_ADDTO(APRUTIL_LDFLAGS, [-L$withval/lib])
+ APR_ADDTO(APRUTIL_INCLUDES, [-I$withval/include -I$withval/../public])
+ fi
+
+ if test "$apu_have_nss" != "1"; then
+ AC_CHECK_HEADERS(nspr/nspr.h nss/nss.h, [nss_have_headers=1])
+ AC_CHECK_LIB(nspr4, PR_Initialize, AC_CHECK_LIB(nss3, PK11_CreatePBEV2AlgorithmID, [nss_have_libs=1],,-lnspr4))
+ if test "$nss_have_headers" != "0" && test "$nss_have_libs" != "0"; then
+ apu_have_nss=1
+ APR_ADDTO(APRUTIL_LDFLAGS, [-L$withval/lib])
+ APR_ADDTO(APRUTIL_INCLUDES, [-I$withval/include -I$withval/../public])
+ fi
+ fi
+
+ CPPFLAGS="$old_cppflags"
+ LDFLAGS="$old_ldflags"
+ fi
+ ], [
+ apu_have_nss=0
+ ])
+
+ AC_SUBST(apu_have_nss)
+
+ dnl Since we have already done the AC_CHECK_LIB tests, if we have it,
+ dnl we know the library is there.
+ if test "$apu_have_nss" = "1"; then
+ LDADD_crypto_nss="$nss_LDFLAGS -lnspr4 -lnss3"
+ apu_have_crypto=1
+ AC_DEFINE([APU_HAVE_CRYPTO], 1, [Define that we have crypto capability])
+ fi
+ AC_SUBST(LDADD_crypto_nss)
+
+ LIBS="$old_libs"
+ CPPFLAGS="$old_cppflags"
+ LDFLAGS="$old_ldflags"
+])
+dnl
diff --git a/build/dso.m4 b/build/dso.m4
index 8f389150..0b77b7a9 100644
--- a/build/dso.m4
+++ b/build/dso.m4
@@ -22,12 +22,14 @@ AC_DEFUN([APU_CHECK_UTIL_DSO], [
AC_ARG_ENABLE([util-dso],
APR_HELP_STRING([--disable-util-dso],
- [disable DSO build of modular components (dbd, ldap)]))
+ [disable DSO build of modular components (crypto, dbd, ldap)]))
if test "$enable_util_dso" = "no"; then
# Statically link the DBD drivers:
objs=
+ test $apu_have_openssl = 1 && objs="$objs crypto/apr_crypto_openssl.lo"
+ test $apu_have_nss = 1 && objs="$objs crypto/apr_crypto_nss.lo"
test $apu_have_oracle = 1 && objs="$objs dbd/apr_dbd_oracle.lo"
test $apu_have_pgsql = 1 && objs="$objs dbd/apr_dbd_pgsql.lo"
test $apu_have_mysql = 1 && objs="$objs dbd/apr_dbd_mysql.lo"
@@ -54,14 +56,18 @@ AC_DEFUN([APU_CHECK_UTIL_DSO], [
done
fi
+ APRUTIL_LIBS="$APRUTIL_LIBS $LDADD_crypto_openssl $LDADD_crypto_nss"
APRUTIL_LIBS="$APRUTIL_LIBS $LDADD_dbd_pgsql $LDADD_dbd_sqlite2 $LDADD_dbd_sqlite3 $LDADD_dbd_oracle $LDADD_dbd_mysql $LDADD_dbd_freetds $LDADD_dbd_odbc"
APRUTIL_LIBS="$APRUTIL_LIBS $LDADD_ldap"
+ APRUTIL_EXPORT_LIBS="$APRUTIL_EXPORT_LIBS $LDADD_crypto_openssl $LDADD_crypto_nss"
APRUTIL_EXPORT_LIBS="$APRUTIL_EXPORT_LIBS $LDADD_dbd_pgsql $LDADD_dbd_sqlite2 $LDADD_dbd_sqlite3 $LDADD_dbd_oracle $LDADD_dbd_mysql $LDADD_dbd_freetds $LDADD_dbd_odbc"
APRUTIL_EXPORT_LIBS="$APRUTIL_EXPORT_LIBS $LDADD_ldap"
else
AC_DEFINE([APU_DSO_BUILD], 1, [Define if modular components are built as DSOs])
dsos=
+ test $apu_have_openssl = 1 && dsos="$dsos crypto/apr_crypto_openssl.la"
+ test $apu_have_nss = 1 && dsos="$dsos crypto/apr_crypto_nss.la"
test $apu_have_oracle = 1 && dsos="$dsos dbd/apr_dbd_oracle.la"
test $apu_have_pgsql = 1 && dsos="$dsos dbd/apr_dbd_pgsql.la"
test $apu_have_mysql = 1 && dsos="$dsos dbd/apr_dbd_mysql.la"
diff --git a/configure.in b/configure.in
index d23db5ee..48899b74 100644
--- a/configure.in
+++ b/configure.in
@@ -13,6 +13,7 @@ sinclude(build/apu-iconv.m4)
sinclude(build/apu-hints.m4)
sinclude(build/apr_common.m4)
sinclude(build/find_apr.m4)
+sinclude(build/crypto.m4)
sinclude(build/dbm.m4)
sinclude(build/dbd.m4)
sinclude(build/dso.m4)
@@ -149,6 +150,9 @@ dnl Find LDAP library
dnl Determine what DBM backend type to use.
dnl Find Expat
dnl Find an iconv library
+APU_CHECK_CRYPTO
+APU_CHECK_CRYPTO_OPENSSL
+APU_CHECK_CRYPTO_NSS
APU_FIND_LDAP
APU_CHECK_DBM
APU_CHECK_DBD
diff --git a/crypto/apr_crypto_nss.c b/crypto/apr_crypto_nss.c
new file mode 100644
index 00000000..bab01880
--- /dev/null
+++ b/crypto/apr_crypto_nss.c
@@ -0,0 +1,766 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apu.h"
+
+#include "apu_config.h"
+#include "apu_errno.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "apr_buckets.h"
+
+#include "apr_crypto_internal.h"
+
+#if APU_HAVE_CRYPTO
+
+#include <prerror.h>
+#include <nss/nss.h>
+#include <nss/pk11pub.h>
+
+struct apr_crypto_config_t {
+};
+
+struct apr_crypto_key_t {
+ CK_MECHANISM_TYPE cipherMech;
+ SECOidTag cipherOid;
+ PK11SymKey *symKey;
+ int ivSize;
+};
+
+struct apr_crypto_block_t {
+ const apr_crypto_t *factory;
+ apr_pool_t *pool;
+ PK11Context *ctx;
+ apr_crypto_key_t *key;
+ int blockSize;
+};
+
+
+/**
+ * Shutdown the crypto library and release resources.
+ *
+ * It is safe to shut down twice.
+ */
+static apr_status_t crypto_shutdown(apr_pool_t *pool)
+{
+ if (NSS_IsInitialized()) {
+ SECStatus s = NSS_Shutdown();
+ if (s != SECSuccess) {
+ return APR_EINIT;
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static apr_status_t crypto_shutdown_helper(void *data)
+{
+ apr_pool_t *pool = (apr_pool_t *) data;
+ return crypto_shutdown(pool);
+}
+
+/**
+ * Initialise the crypto library and perform one time initialisation.
+ */
+static apr_status_t crypto_init(apr_pool_t *pool, const apr_array_header_t *params)
+{
+ SECStatus s;
+ const char *dir = NULL;
+ const char *keyPrefix = NULL;
+ const char *certPrefix = NULL;
+ const char *secmod = NULL;
+ PRUint32 flags = 0;
+ struct apr_crypto_param_t *ents = params ? (struct apr_crypto_param_t *)params->elts : NULL;
+ int i = 0;
+
+ /* sanity check - we can only initialise NSS once */
+ if (NSS_IsInitialized()) {
+ return APR_EREINIT;
+ }
+
+ apr_pool_cleanup_register(pool, pool,
+ crypto_shutdown_helper,
+ apr_pool_cleanup_null);
+
+ for (i = 0; params && i < params->nelts; i++) {
+ switch (ents[i].type) {
+ case APR_CRYPTO_CA_TYPE_DIR:
+ dir = ents[i].path;
+ break;
+ case APR_CRYPTO_CERT_TYPE_KEY3_DB:
+ keyPrefix = ents[i].path;
+ break;
+ case APR_CRYPTO_CA_TYPE_CERT7_DB:
+ certPrefix = ents[i].path;
+ break;
+ case APR_CRYPTO_CA_TYPE_SECMOD:
+ secmod = ents[i].path;
+ break;
+ default:
+ return APR_EINIT;
+ }
+ }
+
+ 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) {
+ return APR_ECRYPT;
+ }
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Clean encryption / decryption context.
+ * @note After cleanup, a context is free to be reused if necessary.
+ * @param driver - driver to use
+ * @param ctx The block context to use.
+ * @return Returns APR_ENOTIMPL if not supported.
+ */
+static apr_status_t crypto_block_cleanup(apr_crypto_block_t *block)
+{
+
+ if (block->ctx) {
+ PK11_DestroyContext(block->ctx, PR_TRUE);
+ block->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);
+}
+
+/**
+ * @brief Clean encryption / decryption factory.
+ * @note After cleanup, a factory is free to be reused if necessary.
+ * @param driver - driver to use
+ * @param f The factory to use.
+ * @return Returns APR_ENOTIMPL if not supported.
+ */
+static apr_status_t crypto_cleanup(apr_crypto_t *f)
+{
+ apr_crypto_key_t *key;
+ if (f->keys) {
+ while ((key = apr_array_pop(f->keys))) {
+ if (key->symKey) {
+ PK11_FreeSymKey(key->symKey);
+ key->symKey = NULL;
+ }
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static apr_status_t crypto_cleanup_helper(void *data)
+{
+ apr_crypto_t *f = (apr_crypto_t *) data;
+ return crypto_cleanup(f);
+}
+
+/**
+ * @brief Create a context for supporting encryption. Keys, certificates,
+ * algorithms and other parameters will be set per context. More than
+ * one context can be created at one time. A cleanup will be automatically
+ * registered with the given pool to guarantee a graceful shutdown.
+ * @param driver - driver to use
+ * @param pool - process pool
+ * @param params - array of key parameters
+ * @param factory - factory pointer will be written here
+ * @return APR_ENOENGINE when the engine specified does not exist. APR_EINITENGINE
+ * if the engine cannot be initialised.
+ */
+static apr_status_t crypto_factory(apr_pool_t *pool,
+ const apr_array_header_t *params,
+ apr_crypto_t **factory)
+{
+ apr_crypto_config_t *config = NULL;
+ /* struct apr_crypto_param_t *ents = params ? (struct apr_crypto_param_t *)params->elts : NULL; */
+ /* int i = 0; */
+ apr_crypto_t *f;
+
+ f = apr_pcalloc(pool, sizeof(apr_crypto_t));
+ if (!f) {
+ return APR_ENOMEM;
+ }
+ *factory = f;
+ f->pool = pool;
+ config = f->config = apr_pcalloc(pool, sizeof(apr_crypto_config_t));
+ if (!config) {
+ return APR_ENOMEM;
+ }
+ f->result = apr_pcalloc(pool, sizeof(apu_err_t));
+ if (!f->result) {
+ return APR_ENOMEM;
+ }
+ f->keys = apr_array_make(pool,
+ 10, sizeof(apr_crypto_key_t));
+
+ apr_pool_cleanup_register(pool, f,
+ crypto_cleanup_helper,
+ apr_pool_cleanup_null);
+
+ /*
+ for (i = 0; params && i < params->nelts; i++) {
+ switch (ents[i].type) {
+ default:
+ f->result->rc = -1;
+ f->result->reason = "The NSS module currently supports "
+ "no per factory initialisation parameters at this time, but "
+ "may do in future.";
+ return APR_EINIT;
+ }
+ }
+ */
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Create a key from the given passphrase. By default, the PBKDF2
+ * algorithm is used to generate the key from the passphrase. It is expected
+ * that the same pass phrase will generate the same key, regardless of the
+ * backend crypto platform used. The key is cleaned up when the context
+ * is cleaned, and may be reused with multiple encryption or decryption
+ * operations.
+ * @note If *key is NULL, a apr_crypto_key_t will be created from a pool. If
+ * *key is not NULL, *key must point at a previously created structure.
+ * @param driver - driver to use
+ * @param p The pool to use.
+ * @param f The context to use.
+ * @param pass The passphrase to use.
+ * @param passLen The passphrase length in bytes
+ * @param salt The salt to use.
+ * @param saltLen The salt length in bytes
+ * @param type 3DES_192, AES_128, AES_192, AES_256.
+ * @param mode Electronic Code Book / Cipher Block Chaining.
+ * @param doPad Pad if necessary.
+ * @param key The key returned, see note.
+ * @param ivSize The size of the initialisation vector will be returned, based
+ * on whether an IV is relevant for this type of crypto.
+ * @return Returns APR_ENOKEY if the pass phrase is missing or empty, or if a backend
+ * error occurred while generating the key. APR_ENOCIPHER if the type or mode
+ * is not supported by the particular backend. APR_EKEYTYPE if the key type is
+ * not known. APR_EPADDING if padding was requested but is not supported.
+ * APR_ENOTIMPL if not implemented.
+ */
+static apr_status_t crypto_passphrase(apr_pool_t *p,
+ const apr_crypto_t *f,
+ const char *pass,
+ apr_size_t passLen,
+ const unsigned char * salt,
+ apr_size_t saltLen,
+ const apr_crypto_block_key_type_e type,
+ const apr_crypto_block_key_mode_e mode,
+ const int doPad,
+ const int iterations,
+ apr_crypto_key_t **k,
+ apr_size_t *ivSize)
+{
+ apr_status_t rv = APR_SUCCESS;
+ PK11SlotInfo * slot;
+ SECItem passItem;
+ SECItem saltItem;
+ SECAlgorithmID *algid;
+ void *wincx = NULL; /* what is wincx? */
+ apr_crypto_key_t *key = *k;
+
+ if (!key) {
+ *k = key = apr_array_push(f->keys);
+ }
+ if (!key) {
+ return APR_ENOMEM;
+ }
+
+ /* decide on what cipher mechanism we will be using */
+ switch (type) {
+
+ case (KEY_3DES_192) :
+ if (MODE_CBC == mode) {
+ key->cipherOid = SEC_OID_DES_EDE3_CBC;
+ }
+ else if (MODE_ECB == mode) {
+ return APR_ENOCIPHER;
+ /* No OID for CKM_DES3_ECB; */
+ }
+ break;
+ case (KEY_AES_128) :
+ if (MODE_CBC == mode) {
+ key->cipherOid = SEC_OID_AES_128_CBC;
+ }
+ else {
+ key->cipherOid = SEC_OID_AES_128_ECB;
+ }
+ break;
+ case (KEY_AES_192) :
+ if (MODE_CBC == mode) {
+ key->cipherOid = SEC_OID_AES_192_CBC;
+ }
+ else {
+ key->cipherOid = SEC_OID_AES_192_ECB;
+ }
+ break;
+ case (KEY_AES_256) :
+ if (MODE_CBC == mode) {
+ key->cipherOid = SEC_OID_AES_256_CBC;
+ }
+ else {
+ key->cipherOid = SEC_OID_AES_256_ECB;
+ }
+ break;
+ default:
+ /* unknown key type, give up */
+ return APR_EKEYTYPE;
+ }
+
+ /* AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD */
+ key->cipherMech = PK11_AlgtagToMechanism(key->cipherOid);
+ if (key->cipherMech == CKM_INVALID_MECHANISM) {
+ return APR_ENOCIPHER;
+ }
+ if (doPad) {
+ CK_MECHANISM_TYPE paddedMech;
+ paddedMech = PK11_GetPadMechanism(key->cipherMech);
+ if (CKM_INVALID_MECHANISM == paddedMech || key->cipherMech == paddedMech) {
+ return APR_EPADDING;
+ }
+ key->cipherMech = paddedMech;
+ }
+
+ /* Turn the raw passphrase and salt into SECItems */
+ passItem.data = (unsigned char*)pass;
+ passItem.len = passLen;
+ saltItem.data = (unsigned char*)salt;
+ saltItem.len = saltLen;
+
+ /* generate the key */
+ /* pbeAlg and cipherAlg are the same. NSS decides the keylength. */
+ algid = PK11_CreatePBEV2AlgorithmID(key->cipherOid, key->cipherOid, SEC_OID_HMAC_SHA1, 0, iterations, &saltItem);
+ if (algid) {
+ slot = PK11_GetBestSlot(key->cipherMech, wincx);
+ if (slot) {
+ key->symKey = PK11_PBEKeyGen(slot, algid, &passItem, PR_FALSE, wincx);
+ PK11_FreeSlot(slot);
+ }
+ 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;
+ }
+ }
+
+ key->ivSize = PK11_GetIVLength(key->cipherMech);
+ if (ivSize) {
+ *ivSize = key->ivSize;
+ }
+
+ return rv;
+}
+
+/**
+ * @brief Initialise a context for encrypting arbitrary data using the given key.
+ * @note If *ctx is NULL, a apr_crypto_block_t will be created from a pool. If
+ * *ctx is not NULL, *ctx must point at a previously created structure.
+ * @param p The pool to use.
+ * @param f The block factory to use.
+ * @param key The key structure.
+ * @param iv Optional initialisation vector. If the buffer pointed to is NULL,
+ * an IV will be created at random, in space allocated from the pool.
+ * If the buffer pointed to is not NULL, the IV in the buffer will be
+ * used.
+ * @param ctx The block context returned, see note.
+ * @param blockSize The block size of the cipher.
+ * @return Returns APR_ENOIV if an initialisation vector is required but not specified.
+ * Returns APR_EINIT if the backend failed to initialise the context. Returns
+ * APR_ENOTIMPL if not implemented.
+ */
+static apr_status_t crypto_block_encrypt_init(apr_pool_t *p,
+ const apr_crypto_t *f,
+ const apr_crypto_key_t *key,
+ const unsigned char **iv,
+ apr_crypto_block_t **ctx,
+ apr_size_t *blockSize)
+{
+ PRErrorCode perr;
+ SECItem * secParam;
+ int usedIvSize;
+ SECItem ivItem;
+ unsigned char * usedIv;
+ apr_crypto_block_t *block = *ctx;
+ if (!block) {
+ *ctx = block = apr_pcalloc(p, sizeof(apr_crypto_block_t));
+ }
+ if (!block) {
+ return APR_ENOMEM;
+ }
+ block->factory = f;
+ block->pool = p;
+
+ 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) {
+ usedIv = apr_pcalloc(p, key->ivSize);
+ if (!usedIv) {
+ return APR_ENOMEM;
+ }
+ SECStatus s = PK11_GenerateRandom(usedIv, key->ivSize);
+ if (s != SECSuccess) {
+ return APR_ENOIV;
+ }
+ *iv = usedIv;
+ }
+ else {
+ usedIv = (unsigned char *)*iv;
+ }
+ ivItem.data = usedIv;
+ ivItem.len = usedIvSize;
+ secParam = PK11_ParamFromIV(key->cipherMech, &ivItem);
+ }
+ else {
+ secParam = PK11_GenerateNewParam(key->cipherMech, key->symKey);
+ }
+ block->blockSize = PK11_GetBlockSize(key->cipherMech, secParam);
+ block->ctx = PK11_CreateContextBySymKey(key->cipherMech, CKA_ENCRYPT, key->symKey, secParam);
+
+ /* did an error occur? */
+ perr = PORT_GetError();
+ if (perr || !block->ctx) {
+ f->result->rc = perr;
+ f->result->msg = PR_ErrorToName(perr);
+ return APR_EINIT;
+ }
+
+ if (blockSize) {
+ *blockSize = PK11_GetBlockSize(key->cipherMech, secParam);
+ }
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Encrypt data provided by in, write it to out.
+ * @note The number of bytes written will be written to outlen. If
+ * out is NULL, outlen will contain the maximum size of the
+ * buffer needed to hold the data, including any data
+ * generated by apr_crypto_block_encrypt_finish below. If *out points
+ * to NULL, a buffer sufficiently large will be created from
+ * the pool provided. If *out points to a not-NULL value, this
+ * value will be used as a buffer instead.
+ * @param ctx The block context to use.
+ * @param out Address of a buffer to which data will be written,
+ * see note.
+ * @param outlen Length of the output will be written here.
+ * @param in Address of the buffer to read.
+ * @param inlen Length of the buffer to read.
+ * @return APR_ECRYPT if an error occurred. Returns APR_ENOTIMPL if
+ * not implemented.
+ */
+static apr_status_t crypto_block_encrypt(apr_crypto_block_t *block,
+ unsigned char **out,
+ apr_size_t *outlen,
+ const unsigned char *in,
+ apr_size_t inlen)
+{
+
+ unsigned char *buffer;
+ int outl = (int) *outlen;
+ if (!out) {
+ *outlen = inlen + block->blockSize;
+ return APR_SUCCESS;
+ }
+ if (!*out) {
+ buffer = apr_palloc(block->pool, inlen + block->blockSize);
+ if (!buffer) {
+ return APR_ENOMEM;
+ }
+ *out = buffer;
+ }
+
+ SECStatus s = PK11_CipherOp(block->ctx, *out, &outl, inlen, (unsigned char*)in, inlen);
+ if (s != SECSuccess) {
+ PRErrorCode perr = PORT_GetError();
+ if (perr) {
+ block->factory->result->rc = perr;
+ block->factory->result->msg = PR_ErrorToName(perr);
+ }
+ return APR_ECRYPT;
+ }
+ *outlen = outl;
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Encrypt final data block, write it to out.
+ * @note If necessary the final block will be written out after being
+ * padded. Typically the final block will be written to the
+ * same buffer used by apr_crypto_block_encrypt, offset by the
+ * number of bytes returned as actually written by the
+ * apr_crypto_block_encrypt() call. After this call, the context
+ * is cleaned and can be reused by apr_crypto_block_encrypt_init().
+ * @param ctx The block context to use.
+ * @param out Address of a buffer to which data will be written. This
+ * buffer must already exist, and is usually the same
+ * buffer used by apr_evp_crypt(). See note.
+ * @param outlen Length of the output will be written here.
+ * @return APR_ECRYPT if an error occurred.
+ * @return APR_EPADDING if padding was enabled and the block was incorrectly
+ * formatted.
+ * @return APR_ENOTIMPL if not implemented.
+ */
+static apr_status_t crypto_block_encrypt_finish(apr_crypto_block_t *block,
+ unsigned char *out,
+ apr_size_t *outlen)
+{
+
+ apr_status_t rv = APR_SUCCESS;
+ unsigned int outl = *outlen;
+
+ SECStatus s = PK11_DigestFinal(block->ctx, out, &outl, block->blockSize);
+ *outlen = outl;
+
+ if (s != SECSuccess) {
+ PRErrorCode perr = PORT_GetError();
+ if (perr) {
+ block->factory->result->rc = perr;
+ block->factory->result->msg = PR_ErrorToName(perr);
+ }
+ rv = APR_ECRYPT;
+ }
+ crypto_block_cleanup(block);
+
+ return rv;
+
+}
+
+/**
+ * @brief Initialise a context for decrypting arbitrary data using the given key.
+ * @note If *ctx is NULL, a apr_crypto_block_t will be created from a pool. If
+ * *ctx is not NULL, *ctx must point at a previously created structure.
+ * @param p The pool to use.
+ * @param f The block factory to use.
+ * @param key The key structure.
+ * @param iv Optional initialisation vector. If the buffer pointed to is NULL,
+ * an IV will be created at random, in space allocated from the pool.
+ * If the buffer pointed to is not NULL, the IV in the buffer will be
+ * used.
+ * @param ctx The block context returned, see note.
+ * @param blockSize The block size of the cipher.
+ * @return Returns APR_ENOIV if an initialisation vector is required but not specified.
+ * Returns APR_EINIT if the backend failed to initialise the context. Returns
+ * APR_ENOTIMPL if not implemented.
+ */
+static apr_status_t crypto_block_decrypt_init(apr_pool_t *p,
+ const apr_crypto_t *f,
+ const apr_crypto_key_t *key,
+ const unsigned char *iv,
+ apr_crypto_block_t **ctx,
+ apr_size_t *blockSize)
+{
+ PRErrorCode perr;
+ SECItem * secParam;
+ apr_crypto_block_t *block = *ctx;
+ if (!block) {
+ *ctx = block = apr_pcalloc(p, sizeof(apr_crypto_block_t));
+ }
+ if (!block) {
+ return APR_ENOMEM;
+ }
+ block->factory = f;
+ block->pool = p;
+
+ 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;
+ secParam = PK11_ParamFromIV(key->cipherMech, &ivItem);
+ }
+ else {
+ secParam = PK11_GenerateNewParam(key->cipherMech, key->symKey);
+ }
+ block->blockSize = PK11_GetBlockSize(key->cipherMech, secParam);
+ block->ctx = PK11_CreateContextBySymKey(key->cipherMech, CKA_DECRYPT, key->symKey, secParam);
+
+ /* did an error occur? */
+ perr = PORT_GetError();
+ if (perr || !block->ctx) {
+ f->result->rc = perr;
+ f->result->msg = PR_ErrorToName(perr);
+ return APR_EINIT;
+ }
+
+ if (blockSize) {
+ *blockSize = PK11_GetBlockSize(key->cipherMech, secParam);
+ }
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Decrypt data provided by in, write it to out.
+ * @note The number of bytes written will be written to outlen. If
+ * out is NULL, outlen will contain the maximum size of the
+ * buffer needed to hold the data, including any data
+ * generated by apr_crypto_block_final below. If *out points
+ * to NULL, a buffer sufficiently large will be created from
+ * the pool provided. If *out points to a not-NULL value, this
+ * value will be used as a buffer instead.
+ * @param ctx The block context to use.
+ * @param out Address of a buffer to which data will be written,
+ * see note.
+ * @param outlen Length of the output will be written here.
+ * @param in Address of the buffer to read.
+ * @param inlen Length of the buffer to read.
+ * @return APR_ECRYPT if an error occurred. Returns APR_ENOTIMPL if
+ * not implemented.
+ */
+static apr_status_t crypto_block_decrypt(apr_crypto_block_t *block,
+ unsigned char **out,
+ apr_size_t *outlen,
+ const unsigned char *in,
+ apr_size_t inlen)
+{
+
+ unsigned char *buffer;
+ int outl = (int) *outlen;
+ if (!out) {
+ *outlen = inlen + block->blockSize;
+ return APR_SUCCESS;
+ }
+ if (!*out) {
+ buffer = apr_palloc(block->pool, inlen + block->blockSize);
+ if (!buffer) {
+ return APR_ENOMEM;
+ }
+ *out = buffer;
+ }
+
+ SECStatus s = PK11_CipherOp(block->ctx, *out, &outl, inlen, (unsigned char*)in, inlen);
+ if (s != SECSuccess) {
+ PRErrorCode perr = PORT_GetError();
+ if (perr) {
+ block->factory->result->rc = perr;
+ block->factory->result->msg = PR_ErrorToName(perr);
+ }
+ return APR_ECRYPT;
+ }
+ *outlen = outl;
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Encrypt final data block, write it to out.
+ * @note If necessary the final block will be written out after being
+ * padded. Typically the final block will be written to the
+ * same buffer used by apr_evp_crypt, offset by the number of
+ * bytes returned as actually written by the apr_evp_crypt()
+ * call. After this call, the context is cleaned and can be
+ * reused by apr_env_encrypt_init() or apr_env_decrypt_init().
+ * @param ctx The block context to use.
+ * @param out Address of a buffer to which data will be written. This
+ * buffer must already exist, and is usually the same
+ * buffer used by apr_evp_crypt(). See note.
+ * @param outlen Length of the output will be written here.
+ * @return APR_ECRYPT if an error occurred.
+ * @return APR_EPADDING if padding was enabled and the block was incorrectly
+ * formatted.
+ * @return APR_ENOTIMPL if not implemented.
+ */
+static apr_status_t crypto_block_decrypt_finish(apr_crypto_block_t *block,
+ unsigned char *out,
+ apr_size_t *outlen)
+{
+
+ apr_status_t rv = APR_SUCCESS;
+ unsigned int outl = *outlen;
+
+ SECStatus s = PK11_DigestFinal(block->ctx, out, &outl, block->blockSize);
+ *outlen = outl;
+
+ if (s != SECSuccess) {
+ PRErrorCode perr = PORT_GetError();
+ if (perr) {
+ block->factory->result->rc = perr;
+ block->factory->result->msg = PR_ErrorToName(perr);
+ }
+ rv = APR_ECRYPT;
+ }
+ crypto_block_cleanup(block);
+
+ return rv;
+
+}
+
+/**
+ * OpenSSL module.
+ */
+APU_MODULE_DECLARE_DATA const apr_crypto_driver_t apr_crypto_nss_driver = {
+ "nss",
+ crypto_init,
+ crypto_factory,
+ 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
+};
+
+#endif
diff --git a/crypto/apr_crypto_openssl.c b/crypto/apr_crypto_openssl.c
new file mode 100644
index 00000000..cc37e470
--- /dev/null
+++ b/crypto/apr_crypto_openssl.c
@@ -0,0 +1,649 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apu.h"
+
+#include "apu_config.h"
+#include "apu_errno.h"
+
+#include <ctype.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "apr_buckets.h"
+
+#include "apr_crypto_internal.h"
+
+#if APU_HAVE_CRYPTO
+
+#include <openssl/evp.h>
+#include <openssl/engine.h>
+
+#define LOG_PREFIX "apr_crypto_openssl: "
+
+struct apr_crypto_config_t {
+ ENGINE *engine;
+};
+
+struct apr_crypto_key_t {
+ const EVP_CIPHER * cipher;
+ unsigned char *key;
+ int keyLen;
+ int doPad;
+ int ivSize;
+};
+
+struct apr_crypto_block_t {
+ const apr_crypto_t *factory;
+ apr_pool_t *pool;
+ EVP_CIPHER_CTX cipherCtx;
+ int initialised;
+ int ivSize;
+ int blockSize;
+ int doPad;
+};
+
+/**
+ * Shutdown the crypto library and release resources.
+ */
+static apr_status_t crypto_shutdown(apr_pool_t *pool) {
+ ERR_free_strings();
+ EVP_cleanup();
+ ENGINE_cleanup();
+ return APR_SUCCESS;
+}
+
+static apr_status_t crypto_shutdown_helper(void *data) {
+ apr_pool_t *pool = (apr_pool_t *) data;
+ return crypto_shutdown(pool);
+}
+
+/**
+ * Initialise the crypto library and perform one time initialisation.
+ */
+static apr_status_t crypto_init(apr_pool_t *pool,
+ const apr_array_header_t *params) {
+ CRYPTO_malloc_init();
+ 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;
+}
+
+/**
+ * @brief Clean encryption / decryption context.
+ * @note After cleanup, a context is free to be reused if necessary.
+ * @param driver - driver to use
+ * @param ctx The block context to use.
+ * @return Returns APR_ENOTIMPL if not supported.
+ */
+static apr_status_t crypto_block_cleanup(apr_crypto_block_t *ctx) {
+
+ if (ctx->initialised) {
+ EVP_CIPHER_CTX_cleanup(&ctx->cipherCtx);
+ ctx->initialised = 0;
+ }
+
+ return APR_SUCCESS;
+
+}
+
+static apr_status_t crypto_block_cleanup_helper(void *data) {
+ apr_crypto_block_t *block = (apr_crypto_block_t *) data;
+ return crypto_block_cleanup(block);
+}
+
+/**
+ * @brief Clean encryption / decryption factory.
+ * @note After cleanup, a factory is free to be reused if necessary.
+ * @param driver - driver to use
+ * @param f The factory to use.
+ * @return Returns APR_ENOTIMPL if not supported.
+ */
+static apr_status_t crypto_cleanup(apr_crypto_t *f) {
+
+ if (f->config->engine) {
+ ENGINE_finish(f->config->engine);
+ ENGINE_free(f->config->engine);
+ f->config->engine = NULL;
+ }
+ return APR_SUCCESS;
+
+}
+
+static apr_status_t crypto_cleanup_helper(void *data) {
+ apr_crypto_t *f = (apr_crypto_t *) data;
+ return crypto_cleanup(f);
+}
+
+/**
+ * @brief Create a context for supporting encryption. Keys, certificates,
+ * algorithms and other parameters will be set per context. More than
+ * one context can be created at one time. A cleanup will be automatically
+ * registered with the given pool to guarantee a graceful shutdown.
+ * @param driver - driver to use
+ * @param pool - process pool
+ * @param params - array of key parameters
+ * @param factory - factory pointer will be written here
+ * @return APR_ENOENGINE when the engine specified does not exist. APR_EINITENGINE
+ * if the engine cannot be initialised.
+ */
+static apr_status_t crypto_factory(apr_pool_t *pool,
+ const apr_array_header_t *params, apr_crypto_t **factory) {
+ apr_crypto_config_t *config = NULL;
+ struct apr_crypto_param_t *ents =
+ params ? (struct apr_crypto_param_t *) params->elts : NULL;
+ int i = 0;
+ apr_crypto_t *f = apr_pcalloc(pool, sizeof(apr_crypto_t));
+ if (!f) {
+ return APR_ENOMEM;
+ }
+ *factory = f;
+ f->pool = pool;
+ config = f->config = apr_pcalloc(pool, sizeof(apr_crypto_config_t));
+ if (!config) {
+ return APR_ENOMEM;
+ }
+ f->result = apr_pcalloc(pool, sizeof(apu_err_t));
+ if (!f->result) {
+ return APR_ENOMEM;
+ }
+ f->keys = apr_array_make(pool, 10, sizeof(apr_crypto_key_t));
+
+ apr_pool_cleanup_register(pool, f, crypto_cleanup_helper,
+ apr_pool_cleanup_null);
+
+ for (i = 0; params && i < params->nelts; i++) {
+ switch (ents[i].type) {
+ case APR_CRYPTO_ENGINE:
+ config->engine = ENGINE_by_id(ents[i].path);
+ if (!config->engine) {
+ return APR_ENOENGINE;
+ }
+ if (!ENGINE_init(config->engine)) {
+ ENGINE_free(config->engine);
+ config->engine = NULL;
+ return APR_EINITENGINE;
+ }
+ break;
+ }
+ }
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Create a key from the given passphrase. By default, the PBKDF2
+ * algorithm is used to generate the key from the passphrase. It is expected
+ * that the same pass phrase will generate the same key, regardless of the
+ * backend crypto platform used. The key is cleaned up when the context
+ * is cleaned, and may be reused with multiple encryption or decryption
+ * operations.
+ * @note If *key is NULL, a apr_crypto_key_t will be created from a pool. If
+ * *key is not NULL, *key must point at a previously created structure.
+ * @param driver - driver to use
+ * @param p The pool to use.
+ * @param f The context to use.
+ * @param pass The passphrase to use.
+ * @param passLen The passphrase length in bytes
+ * @param salt The salt to use.
+ * @param saltLen The salt length in bytes
+ * @param type 3DES_192, AES_128, AES_192, AES_256.
+ * @param mode Electronic Code Book / Cipher Block Chaining.
+ * @param doPad Pad if necessary.
+ * @param key The key returned, see note.
+ * @param ivSize The size of the initialisation vector will be returned, based
+ * on whether an IV is relevant for this type of crypto.
+ * @return Returns APR_ENOKEY if the pass phrase is missing or empty, or if a backend
+ * error occurred while generating the key. APR_ENOCIPHER if the type or mode
+ * is not supported by the particular backend. APR_EKEYTYPE if the key type is
+ * not known. APR_EPADDING if padding was requested but is not supported.
+ * APR_ENOTIMPL if not implemented.
+ */
+static apr_status_t crypto_passphrase(apr_pool_t *p, const apr_crypto_t *f,
+ const char *pass, apr_size_t passLen, const unsigned char * salt,
+ apr_size_t saltLen, const apr_crypto_block_key_type_e type,
+ const apr_crypto_block_key_mode_e mode, const int doPad,
+ const int iterations, apr_crypto_key_t **k, apr_size_t *ivSize) {
+ apr_crypto_key_t *key = *k;
+
+ if (!key) {
+ *k = key = apr_array_push(f->keys);
+ }
+ if (!key) {
+ return APR_ENOMEM;
+ }
+
+ /* determine the cipher to be used */
+ switch (type) {
+
+ case (KEY_3DES_192):
+
+ /* A 3DES key */
+ if (mode == MODE_CBC) {
+ key->cipher = EVP_des_ede3_cbc();
+ } else {
+ key->cipher = EVP_des_ede3_ecb();
+ }
+ break;
+
+ case (KEY_AES_128):
+
+ if (mode == MODE_CBC) {
+ key->cipher = EVP_aes_128_cbc();
+ } else {
+ key->cipher = EVP_aes_128_ecb();
+ }
+ break;
+
+ case (KEY_AES_192):
+
+ if (mode == MODE_CBC) {
+ key->cipher = EVP_aes_192_cbc();
+ } else {
+ key->cipher = EVP_aes_192_ecb();
+ }
+ break;
+
+ case (KEY_AES_256):
+
+ if (mode == MODE_CBC) {
+ key->cipher = EVP_aes_256_cbc();
+ } else {
+ key->cipher = EVP_aes_256_ecb();
+ }
+ break;
+
+ default:
+
+ /* unknown key type, give up */
+ return APR_EKEYTYPE;
+
+ }
+
+ /* find the length of the key we need */
+ key->keyLen = EVP_CIPHER_key_length(key->cipher);
+
+ /* make space for the key */
+ key->key = apr_pcalloc(p, key->keyLen);
+ if (!key->key) {
+ return APR_ENOMEM;
+ }
+
+ /* generate the key */
+ if (PKCS5_PBKDF2_HMAC_SHA1(pass, passLen, (unsigned char *) salt, saltLen,
+ iterations, key->keyLen, key->key) == 0) {
+ return APR_ENOKEY;
+ }
+
+ key->doPad = doPad;
+
+ /* note: openssl incorrectly returns non zero IV size values for ECB
+ * algorithms, so work around this by ignoring the IV size.
+ */
+ if (MODE_ECB != mode) {
+ key->ivSize = EVP_CIPHER_iv_length(key->cipher);
+ }
+ if (ivSize) {
+ *ivSize = key->ivSize;
+ }
+
+ return APR_SUCCESS;
+}
+
+/**
+ * @brief Initialise a context for encrypting arbitrary data using the given key.
+ * @note If *ctx is NULL, a apr_crypto_block_t will be created from a pool. If
+ * *ctx is not NULL, *ctx must point at a previously created structure.
+ * @param p The pool to use.
+ * @param f The block factory to use.
+ * @param type 3DES_192, AES_128, AES_192, AES_256.
+ * @param mode Electronic Code Book / Cipher Block Chaining.
+ * @param key The key
+ * @param keyLen The key length in bytes
+ * @param iv Optional initialisation vector.
+ * @param doPad Pad if necessary.
+ * @param ctx The block context returned, see note.
+ * @param blockSize The block size of the cipher.
+ * @return Returns APR_ENOIV if an initialisation vector is required but not specified.
+ * Returns APR_EINIT if the backend failed to initialise the context. Returns
+ * APR_ENOTIMPL if not implemented.
+ */
+static apr_status_t crypto_block_encrypt_init(apr_pool_t *p,
+ const apr_crypto_t *f, const apr_crypto_key_t *key,
+ const unsigned char **iv, apr_crypto_block_t **ctx,
+ apr_size_t *blockSize) {
+ unsigned char *usedIv;
+ apr_crypto_config_t *config = f->config;
+ apr_crypto_block_t *block = *ctx;
+ if (!block) {
+ *ctx = block = apr_pcalloc(p, sizeof(apr_crypto_block_t));
+ }
+ if (!block) {
+ return APR_ENOMEM;
+ }
+ block->factory = f;
+ block->pool = p;
+
+ apr_pool_cleanup_register(p, block, crypto_block_cleanup_helper,
+ apr_pool_cleanup_null);
+
+ /* create a new context for encryption */
+ EVP_CIPHER_CTX_init(&block->cipherCtx);
+ block->initialised = 1;
+
+ /* generate an IV, if necessary */
+ usedIv = NULL;
+ if (key->ivSize) {
+ if (iv == NULL) {
+ return APR_ENOIV;
+ }
+ if (*iv == NULL) {
+ usedIv = apr_pcalloc(p, key->ivSize);
+ if (!usedIv) {
+ return APR_ENOMEM;
+ }
+ if (!((RAND_status() == 1)
+ && (RAND_bytes(usedIv, key->ivSize) == 1))) {
+ return APR_ENOIV;
+ }
+ *iv = usedIv;
+ } else {
+ usedIv = (unsigned char *) *iv;
+ }
+ }
+
+ /* set up our encryption context */
+#if CRYPTO_OPENSSL_CONST_BUFFERS
+ if (!EVP_EncryptInit_ex(&block->cipherCtx, key->cipher, config->engine,
+ key->key, usedIv)) {
+#else
+ if (!EVP_EncryptInit_ex(&block->cipherCtx, key->cipher, config->engine, (unsigned char *) key->key, (unsigned char *) usedIv)) {
+#endif
+ return APR_EINIT;
+ }
+
+ /* Clear up any read padding */
+ if (!EVP_CIPHER_CTX_set_padding(&block->cipherCtx, key->doPad)) {
+ return APR_EPADDING;
+ }
+
+ if (blockSize) {
+ *blockSize = EVP_CIPHER_block_size(key->cipher);
+ }
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Encrypt data provided by in, write it to out.
+ * @note The number of bytes written will be written to outlen. If
+ * out is NULL, outlen will contain the maximum size of the
+ * buffer needed to hold the data, including any data
+ * generated by apr_crypto_block_encrypt_finish below. If *out points
+ * to NULL, a buffer sufficiently large will be created from
+ * the pool provided. If *out points to a not-NULL value, this
+ * value will be used as a buffer instead.
+ * @param ctx The block context to use.
+ * @param out Address of a buffer to which data will be written,
+ * see note.
+ * @param outlen Length of the output will be written here.
+ * @param in Address of the buffer to read.
+ * @param inlen Length of the buffer to read.
+ * @return APR_ECRYPT if an error occurred. Returns APR_ENOTIMPL if
+ * not implemented.
+ */
+static apr_status_t crypto_block_encrypt(apr_crypto_block_t *ctx,
+ unsigned char **out, apr_size_t *outlen, const unsigned char *in,
+ apr_size_t inlen) {
+ 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(ctx->pool, inlen + EVP_MAX_BLOCK_LENGTH);
+ if (!buffer) {
+ return APR_ENOMEM;
+ }
+ *out = buffer;
+ }
+
+#if CRYPT_OPENSSL_CONST_BUFFERS
+ if (!EVP_EncryptUpdate(&ctx->cipherCtx, (*out), &outl, in, inlen)) {
+#else
+ if (!EVP_EncryptUpdate(&ctx->cipherCtx, (*out), &outl,
+ (unsigned char *) in, inlen)) {
+#endif
+ return APR_ECRYPT;
+ }
+ *outlen = outl;
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Encrypt final data block, write it to out.
+ * @note If necessary the final block will be written out after being
+ * padded. Typically the final block will be written to the
+ * same buffer used by apr_crypto_block_encrypt, offset by the
+ * number of bytes returned as actually written by the
+ * apr_crypto_block_encrypt() call. After this call, the context
+ * is cleaned and can be reused by apr_crypto_block_encrypt_init().
+ * @param ctx The block context to use.
+ * @param out Address of a buffer to which data will be written. This
+ * buffer must already exist, and is usually the same
+ * buffer used by apr_evp_crypt(). See note.
+ * @param outlen Length of the output will be written here.
+ * @return APR_ECRYPT if an error occurred.
+ * @return APR_EPADDING if padding was enabled and the block was incorrectly
+ * formatted.
+ * @return APR_ENOTIMPL if not implemented.
+ */
+static apr_status_t crypto_block_encrypt_finish(apr_crypto_block_t *ctx,
+ unsigned char *out, apr_size_t *outlen) {
+ int len = *outlen;
+
+ if (EVP_EncryptFinal_ex(&ctx->cipherCtx, out, &len) == 0) {
+ return APR_EPADDING;
+ }
+ *outlen = len;
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Initialise a context for decrypting arbitrary data using the given key.
+ * @note If *ctx is NULL, a apr_crypto_block_t will be created from a pool. If
+ * *ctx is not NULL, *ctx must point at a previously created structure.
+ * @param p The pool to use.
+ * @param f The block factory to use.
+ * @param key The key structure.
+ * @param iv Optional initialisation vector. If the buffer pointed to is NULL,
+ * an IV will be created at random, in space allocated from the pool.
+ * If the buffer is not NULL, the IV in the buffer will be used.
+ * @param ctx The block context returned, see note.
+ * @param blockSize The block size of the cipher.
+ * @return Returns APR_ENOIV if an initialisation vector is required but not specified.
+ * Returns APR_EINIT if the backend failed to initialise the context. Returns
+ * APR_ENOTIMPL if not implemented.
+ */
+static apr_status_t crypto_block_decrypt_init(apr_pool_t *p,
+ const apr_crypto_t *f, const apr_crypto_key_t *key,
+ const unsigned char *iv, apr_crypto_block_t **ctx,
+ apr_size_t *blockSize) {
+ apr_crypto_config_t *config = f->config;
+ apr_crypto_block_t *block = *ctx;
+ if (!block) {
+ *ctx = block = apr_pcalloc(p, sizeof(apr_crypto_block_t));
+ }
+ if (!block) {
+ return APR_ENOMEM;
+ }
+ block->factory = f;
+ block->pool = p;
+
+ apr_pool_cleanup_register(p, block, crypto_block_cleanup_helper,
+ apr_pool_cleanup_null);
+
+ /* create a new context for encryption */
+ EVP_CIPHER_CTX_init(&block->cipherCtx);
+ block->initialised = 1;
+
+ /* generate an IV, if necessary */
+ if (key->ivSize) {
+ if (iv == NULL) {
+ return APR_ENOIV;
+ }
+ }
+
+ /* set up our encryption context */
+#if CRYPTO_OPENSSL_CONST_BUFFERS
+ if (!EVP_DecryptInit_ex(&block->cipherCtx, key->cipher, config->engine,
+ key->key, iv)) {
+#else
+ if (!EVP_DecryptInit_ex(&block->cipherCtx, key->cipher, config->engine, (unsigned char *) key->key, (unsigned char *) iv)) {
+#endif
+ return APR_EINIT;
+ }
+
+ /* Clear up any read padding */
+ if (!EVP_CIPHER_CTX_set_padding(&block->cipherCtx, key->doPad)) {
+ return APR_EPADDING;
+ }
+
+ if (blockSize) {
+ *blockSize = EVP_CIPHER_block_size(key->cipher);
+ }
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Decrypt data provided by in, write it to out.
+ * @note The number of bytes written will be written to outlen. If
+ * out is NULL, outlen will contain the maximum size of the
+ * buffer needed to hold the data, including any data
+ * generated by apr_crypto_block_final below. If *out points
+ * to NULL, a buffer sufficiently large will be created from
+ * the pool provided. If *out points to a not-NULL value, this
+ * value will be used as a buffer instead.
+ * @param ctx The block context to use.
+ * @param out Address of a buffer to which data will be written,
+ * see note.
+ * @param outlen Length of the output will be written here.
+ * @param in Address of the buffer to read.
+ * @param inlen Length of the buffer to read.
+ * @return APR_ECRYPT if an error occurred. Returns APR_ENOTIMPL if
+ * not implemented.
+ */
+static apr_status_t crypto_block_decrypt(apr_crypto_block_t *ctx,
+ unsigned char **out, apr_size_t *outlen, const unsigned char *in,
+ apr_size_t inlen) {
+
+ 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(ctx->pool, inlen + EVP_MAX_BLOCK_LENGTH);
+ if (!buffer) {
+ return APR_ENOMEM;
+ }
+ *out = buffer;
+ }
+
+#if CRYPT_OPENSSL_CONST_BUFFERS
+ if (!EVP_DecryptUpdate(&ctx->cipherCtx, *out, &outl, in, inlen)) {
+#else
+ if (!EVP_DecryptUpdate(&ctx->cipherCtx, *out, &outl, (unsigned char *) in,
+ inlen)) {
+#endif
+ return APR_ECRYPT;
+ }
+ *outlen = outl;
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * @brief Decrypt final data block, write it to out.
+ * @note If necessary the final block will be written out after being
+ * padded. Typically the final block will be written to the
+ * same buffer used by apr_evp_crypt, offset by the number of
+ * bytes returned as actually written by the apr_evp_crypt()
+ * call. After this call, the context is cleaned and can be
+ * reused by apr_env_encrypt_init() or apr_env_decrypt_init().
+ * @param ctx The block context to use.
+ * @param out Address of a buffer to which data will be written. This
+ * buffer must already exist, and is usually the same
+ * buffer used by apr_evp_crypt(). See note.
+ * @param outlen Length of the output will be written here.
+ * @return APR_ECRYPT if an error occurred.
+ * @return APR_EPADDING if padding was enabled and the block was incorrectly
+ * formatted.
+ * @return APR_ENOTIMPL if not implemented.
+ */
+static apr_status_t crypto_block_decrypt_finish(apr_crypto_block_t *ctx,
+ unsigned char *out, apr_size_t *outlen) {
+
+ int len = *outlen;
+
+ if (EVP_DecryptFinal_ex(&ctx->cipherCtx, out, &len) == 0) {
+ return APR_EPADDING;
+ }
+ *outlen = len;
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * OpenSSL module.
+ */
+APU_MODULE_DECLARE_DATA const apr_crypto_driver_t apr_crypto_openssl_driver = {
+ "openssl", crypto_init, crypto_factory, 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 };
+
+#endif
diff --git a/test/Makefile.in b/test/Makefile.in
index 9b6890a8..beb06d44 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -17,7 +17,7 @@ STDTEST_PORTABLE = testall dbd
TESTS = teststrmatch.lo testuri.lo testuuid.lo testbuckets.lo testpass.lo \
testmd4.lo testmd5.lo testldap.lo testdate.lo testdbm.lo testdbd.lo \
testxml.lo testrmm.lo testreslist.lo testqueue.lo testxlate.lo \
- testmemcache.lo
+ testmemcache.lo testcrypto.lo
PROGRAMS = $(STDTEST_PORTABLE)
@@ -62,7 +62,7 @@ check: $(TESTALL_COMPONENTS) $(STDTEST_PORTABLE) $(STDTEST_NONPORTABLE)
for prog in $(STDTEST_PORTABLE) $(STDTEST_NONPORTABLE); do \
if test "$$prog" = 'dbd'; then \
for driver in sqlite2 sqlite3; do \
- @apr_shlibpath_var@="`echo "../dbd/.libs:../ldap/.libs:$$@apr_shlibpath_var@" | sed -e 's/::*$$//'`" \
+ @apr_shlibpath_var@="`echo "../crypto/.libs:../dbd/.libs:../ldap/.libs:$$@apr_shlibpath_var@" | sed -e 's/::*$$//'`" \
./$$prog $$driver; \
status=$$?; \
if test $$status != 0; then \
@@ -71,7 +71,7 @@ check: $(TESTALL_COMPONENTS) $(STDTEST_PORTABLE) $(STDTEST_NONPORTABLE)
fi; \
done; \
else \
- @apr_shlibpath_var@="`echo "../dbd/.libs:../ldap/.libs:$$@apr_shlibpath_var@" | sed -e 's/::*$$//'`" \
+ @apr_shlibpath_var@="`echo "../crypto/.libs:../dbd/.libs:../ldap/.libs:$$@apr_shlibpath_var@" | sed -e 's/::*$$//'`" \
./$$prog; \
status=$$?; \
if test $$status != 0; then \
diff --git a/test/abts_tests.h b/test/abts_tests.h
index 040d02db..b612d31b 100644
--- a/test/abts_tests.h
+++ b/test/abts_tests.h
@@ -30,6 +30,7 @@ const struct testlist {
{testpass},
{testmd4},
{testmd5},
+ {testcrypto},
{testldap},
{testdbd},
{testdate},
diff --git a/test/testcrypto.c b/test/testcrypto.c
new file mode 100644
index 00000000..8528dae2
--- /dev/null
+++ b/test/testcrypto.c
@@ -0,0 +1,698 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr.h"
+#include "apu.h"
+#include "apu_errno.h"
+#include "apr_pools.h"
+#include "apr_dso.h"
+#include "apr_crypto.h"
+#include "apr_strings.h"
+
+#if APU_HAVE_CRYPTO
+
+#define TEST_STRING "12345"
+#define ALIGNED_STRING "123456789012345"
+
+static const apr_crypto_driver_t *get_driver(abts_case *tc, apr_pool_t *pool,
+ const char *name, const apr_array_header_t *params) {
+
+ const apr_crypto_driver_t *driver = NULL;
+ const apu_err_t *err = NULL;
+ apr_status_t rv;
+
+ rv = apr_crypto_init(pool, params);
+ ABTS_ASSERT(tc, "failed to init apr_crypto", rv == APR_SUCCESS);
+
+ rv = apr_crypto_get_driver(pool, name, &driver, params, &err);
+ if (APR_SUCCESS != rv && err) {
+ ABTS_NOT_IMPL(tc, err->msg);
+ return NULL;
+ }
+ if (APR_ENOTIMPL == rv) {
+ ABTS_NOT_IMPL(tc, (char *)driver);
+ return NULL;
+ }
+ ABTS_ASSERT(tc, "failed to apr_crypto_get_driver", rv == APR_SUCCESS);
+ ABTS_ASSERT(tc, "apr_crypto_get_driver returned NULL", driver != NULL);
+ if (!driver || rv) {
+ return NULL;
+ }
+
+ return driver;
+
+}
+
+static const apr_crypto_driver_t *get_nss_driver(abts_case *tc,
+ apr_pool_t *pool) {
+
+ apr_array_header_t *params;
+ apr_crypto_param_t *param;
+
+ /* initialise NSS */
+ params = apr_array_make(pool, 10, sizeof(apr_crypto_param_t));
+ param = apr_array_push(params);
+ param->type = APR_CRYPTO_CA_TYPE_DIR;
+ param->path = "data";
+ return get_driver(tc, pool, "nss", params);
+
+}
+
+static const apr_crypto_driver_t *get_openssl_driver(abts_case *tc,
+ apr_pool_t *pool) {
+
+ return get_driver(tc, pool, "openssl", NULL);
+
+}
+
+static apr_crypto_t *factory(abts_case *tc, apr_pool_t *pool,
+ const apr_crypto_driver_t *driver) {
+
+ apr_crypto_t *f = NULL;
+
+ if (!driver) {
+ return NULL;
+ }
+
+ /* get the factory */
+ apr_crypto_factory(driver, pool, NULL, &f);
+ ABTS_ASSERT(tc, "apr_crypto_factory returned NULL", f != NULL);
+
+ return f;
+
+}
+
+static const apr_crypto_key_t *passphrase(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,
+ int doPad, const char *description) {
+
+ apr_crypto_key_t *key = NULL;
+ const char *pass = "secret";
+ const char *salt = "salt";
+ apr_status_t rv;
+
+ if (!driver || !f) {
+ return NULL;
+ }
+
+ /* init the passphrase */
+ rv = apr_crypto_passphrase(driver, pool, f, pass, strlen(pass),
+ (unsigned char *) salt, strlen(salt), type, mode, doPad, 4096,
+ &key, NULL);
+ if (APR_ENOCIPHER == rv) {
+ ABTS_NOT_IMPL(tc, apr_psprintf(pool, "skipped: %s %s passphrase return APR_ENOCIPHER: error %d: %s (%s)\n", description, apr_crypto_driver_name(driver), f->result->rc, f->result->reason ? f->result->reason : "", f->result->msg ? f->result->msg : ""));
+ return NULL;
+ } else {
+ if (APR_SUCCESS != rv) {
+ fprintf(stderr, "passphrase: %s %s native error %d: %s (%s)\n",
+ description, apr_crypto_driver_name(driver), f->result->rc,
+ f->result->reason ? f->result->reason : "",
+ f->result->msg ? f->result->msg : "");
+ }
+ ABTS_ASSERT(tc, "apr_crypto_passphrase returned APR_ENOKEY", rv != APR_ENOKEY);
+ ABTS_ASSERT(tc, "apr_crypto_passphrase returned APR_EPADDING", rv != APR_EPADDING);
+ ABTS_ASSERT(tc, "apr_crypto_passphrase returned APR_EKEYTYPE", rv != APR_EKEYTYPE);
+ ABTS_ASSERT(tc, "failed to apr_crypto_passphrase", rv == APR_SUCCESS);
+ ABTS_ASSERT(tc, "apr_crypto_passphrase returned NULL context", key != NULL);
+ }
+ if (rv) {
+ return NULL;
+ }
+ return key;
+
+}
+
+static unsigned char *encrypt_block(abts_case *tc, apr_pool_t *pool,
+ const apr_crypto_driver_t *driver, const apr_crypto_t *f,
+ const apr_crypto_key_t *key, const unsigned char *in,
+ const apr_size_t inlen, unsigned char **cipherText,
+ apr_size_t *cipherTextLen, const unsigned char **iv,
+ apr_size_t *blockSize, const char *description) {
+
+ apr_crypto_block_t *block = NULL;
+ apr_size_t len = 0;
+ apr_status_t rv;
+
+ if (!driver || !f || !key || !in) {
+ return NULL;
+ }
+
+ /* init the encryption */
+ rv = apr_crypto_block_encrypt_init(driver, pool, f, key, iv, &block,
+ blockSize);
+ if (APR_ENOTIMPL == rv) {
+ ABTS_NOT_IMPL(tc, "apr_crypto_block_encrypt_init returned APR_ENOTIMPL");
+ } else {
+ if (APR_SUCCESS != rv) {
+ fprintf(stderr, "encrypt_init: %s %s native error %d: %s (%s)\n",
+ description, apr_crypto_driver_name(driver), f->result->rc,
+ f->result->reason ? f->result->reason : "",
+ f->result->msg ? f->result->msg : "");
+ }
+ ABTS_ASSERT(tc, "apr_crypto_block_encrypt_init returned APR_ENOKEY", rv != APR_ENOKEY);
+ ABTS_ASSERT(tc, "apr_crypto_block_encrypt_init returned APR_ENOIV", rv != APR_ENOIV);
+ ABTS_ASSERT(tc, "apr_crypto_block_encrypt_init returned APR_EKEYTYPE", rv != APR_EKEYTYPE);
+ ABTS_ASSERT(tc, "apr_crypto_block_encrypt_init returned APR_EKEYLENGTH", rv != APR_EKEYLENGTH);
+ ABTS_ASSERT(tc, "failed to apr_crypto_block_encrypt_init", rv == APR_SUCCESS);
+ ABTS_ASSERT(tc, "apr_crypto_block_encrypt_init returned NULL context", block != NULL);
+ }
+ if (!block || rv) {
+ return NULL;
+ }
+
+ /* encrypt the block */
+ rv = apr_crypto_block_encrypt(driver, block, cipherText + len,
+ cipherTextLen, in, inlen);
+ if (APR_SUCCESS != rv) {
+ fprintf(stderr, "encrypt: %s %s native error %d: %s (%s)\n",
+ description, apr_crypto_driver_name(driver), f->result->rc,
+ f->result->reason ? f->result->reason : "",
+ f->result->msg ? f->result->msg : "");
+ }
+ ABTS_ASSERT(tc, "apr_crypto_block_encrypt returned APR_ECRYPT", rv != APR_ECRYPT);
+ ABTS_ASSERT(tc, "failed to apr_crypto_block_encrypt", rv == APR_SUCCESS);
+ ABTS_ASSERT(tc, "apr_crypto_block_encrypt failed to allocate buffer", *cipherText != NULL);
+ if (rv) {
+ return NULL;
+ }
+
+ /* finalise the encryption */
+ rv = apr_crypto_block_encrypt_finish(driver, block, *cipherText
+ + *cipherTextLen, &len);
+ if (APR_SUCCESS != rv) {
+ fprintf(stderr, "encrypt_finish: %s %s native error %d: %s (%s)\n",
+ description, apr_crypto_driver_name(driver), f->result->rc,
+ f->result->reason ? f->result->reason : "",
+ f->result->msg ? f->result->msg : "");
+ }
+ ABTS_ASSERT(tc, "apr_crypto_block_encrypt_finish returned APR_ECRYPT", rv != APR_ECRYPT);
+ ABTS_ASSERT(tc, "apr_crypto_block_encrypt_finish returned APR_EPADDING", rv != APR_EPADDING);
+ ABTS_ASSERT(tc, "failed to apr_crypto_block_encrypt_finish", rv == APR_SUCCESS);
+ *cipherTextLen += len;
+ apr_crypto_block_cleanup(driver, block);
+ if (rv) {
+ return NULL;
+ }
+
+ return *cipherText;
+
+}
+
+static unsigned char *decrypt_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, unsigned char *cipherText,
+ apr_size_t cipherTextLen, unsigned char **plainText,
+ apr_size_t *plainTextLen, const unsigned char *iv,
+ apr_size_t *blockSize, const char *description) {
+
+ apr_crypto_block_t *block = NULL;
+ apr_size_t len = 0;
+ apr_status_t rv;
+
+ if (!driver || !f || !key || !cipherText) {
+ return NULL;
+ }
+
+ /* init the decryption */
+ rv = apr_crypto_block_decrypt_init(driver, pool, f, key, iv, &block,
+ blockSize);
+ if (APR_ENOTIMPL == rv) {
+ ABTS_NOT_IMPL(tc, "apr_crypto_block_decrypt_init returned APR_ENOTIMPL");
+ } else {
+ if (APR_SUCCESS != rv) {
+ fprintf(stderr, "decrypt_init: %s %s native error %d: %s (%s)\n",
+ description, apr_crypto_driver_name(driver), f->result->rc,
+ f->result->reason ? f->result->reason : "",
+ f->result->msg ? f->result->msg : "");
+ }
+ ABTS_ASSERT(tc, "apr_crypto_block_decrypt_init returned APR_ENOKEY", rv != APR_ENOKEY);
+ ABTS_ASSERT(tc, "apr_crypto_block_decrypt_init returned APR_ENOIV", rv != APR_ENOIV);
+ ABTS_ASSERT(tc, "apr_crypto_block_decrypt_init returned APR_EKEYTYPE", rv != APR_EKEYTYPE);
+ ABTS_ASSERT(tc, "apr_crypto_block_decrypt_init returned APR_EKEYLENGTH", rv != APR_EKEYLENGTH);
+ ABTS_ASSERT(tc, "failed to apr_crypto_block_decrypt_init", rv == APR_SUCCESS);
+ ABTS_ASSERT(tc, "apr_crypto_block_decrypt_init returned NULL context", block != NULL);
+ }
+ if (!block || rv) {
+ return NULL;
+ }
+
+ /* decrypt the block */
+ rv = apr_crypto_block_decrypt(driver, block, plainText, plainTextLen,
+ cipherText, cipherTextLen);
+ if (APR_SUCCESS != rv) {
+ fprintf(stderr, "decrypt: %s %s native error %d: %s (%s)\n",
+ description, apr_crypto_driver_name(driver), f->result->rc,
+ f->result->reason ? f->result->reason : "",
+ f->result->msg ? f->result->msg : "");
+ }
+ ABTS_ASSERT(tc, "apr_crypto_block_decrypt returned APR_ECRYPT", rv != APR_ECRYPT);
+ ABTS_ASSERT(tc, "failed to apr_crypto_block_decrypt", rv == APR_SUCCESS);
+ ABTS_ASSERT(tc, "apr_crypto_block_decrypt failed to allocate buffer", *plainText != NULL);
+ if (rv) {
+ return NULL;
+ }
+
+ /* finalise the decryption */
+ rv = apr_crypto_block_decrypt_finish(driver, block, *plainText
+ + *plainTextLen, &len);
+ if (APR_SUCCESS != rv) {
+ fprintf(stderr, "decrypt_finish: %s %s native error %d: %s (%s)\n",
+ description, apr_crypto_driver_name(driver), f->result->rc,
+ f->result->reason ? f->result->reason : "",
+ f->result->msg ? f->result->msg : "");
+ }
+ ABTS_ASSERT(tc, "apr_crypto_block_decrypt_finish returned APR_ECRYPT", rv != APR_ECRYPT);
+ ABTS_ASSERT(tc, "apr_crypto_block_decrypt_finish returned APR_EPADDING", rv != APR_EPADDING);
+ ABTS_ASSERT(tc, "failed to apr_crypto_block_decrypt_finish", rv == APR_SUCCESS);
+ if (rv) {
+ return NULL;
+ }
+
+ *plainTextLen += len;
+ apr_crypto_block_cleanup(driver, block);
+
+ return *plainText;
+
+}
+
+/**
+ * Interoperability test.
+ *
+ * data must point at an array of two driver structures. Data will be encrypted
+ * with the first driver, and decrypted with the second.
+ *
+ * If the two drivers interoperate, the test passes.
+ */
+static void crypto_block_cross(abts_case *tc, apr_pool_t *pool,
+ const apr_crypto_driver_t **drivers,
+ 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, 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 *key1 = NULL;
+ const apr_crypto_key_t *key2 = NULL;
+
+ unsigned char *cipherText = NULL;
+ apr_size_t cipherTextLen = 0;
+ unsigned char *plainText = NULL;
+ apr_size_t plainTextLen = 0;
+ const unsigned char *iv = NULL;
+ apr_size_t blockSize = 0;
+
+ f1 = factory(tc, pool, driver1);
+ f2 = factory(tc, pool, driver2);
+ key1 = passphrase(tc, pool, driver1, f1, type, mode, doPad, description);
+ key2 = passphrase(tc, pool, driver2, f2, type, mode, doPad, description);
+
+ cipherText = encrypt_block(tc, pool, driver1, f1, key1, in, inlen,
+ &cipherText, &cipherTextLen, &iv, &blockSize, description);
+ plainText = decrypt_block(tc, pool, driver2, f2, key2, cipherText,
+ cipherTextLen, &plainText, &plainTextLen, iv, &blockSize,
+ description);
+
+ if (cipherText && plainText) {
+ if (memcmp(in, plainText, inlen)) {
+ fprintf(stderr, "cross mismatch: %s %s/%s\n", description,
+ apr_crypto_driver_name(driver1), apr_crypto_driver_name(
+ driver2));
+ }
+ ABTS_STR_EQUAL(tc, (char *)in, (char *)plainText);
+ }
+
+}
+
+/**
+ * Test initialisation.
+ */
+static void test_crypto_init(abts_case *tc, void *data) {
+ apr_pool_t *pool = NULL;
+ apr_status_t rv;
+
+ apr_pool_create(&pool, NULL);
+
+ rv = apr_crypto_init(pool, NULL);
+ ABTS_ASSERT(tc, "failed to init apr_crypto", rv == APR_SUCCESS);
+
+ apr_pool_destroy(pool);
+
+}
+
+/**
+ * Simple test of OpenSSL block crypt.
+ */
+static void test_crypto_block_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_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_CBC, 0, in, inlen,
+ "KEY_3DES_192/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_ECB, 0, in, inlen,
+ "KEY_3DES_192/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_CBC, 0, in, inlen,
+ "KEY_AES_256/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_ECB, 0, in, inlen,
+ "KEY_AES_256/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_CBC, 0, in, inlen,
+ "KEY_AES_192/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_ECB, 0, in, inlen,
+ "KEY_AES_192/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_CBC, 0, in, inlen,
+ "KEY_AES_128/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_ECB, 0, in, inlen,
+ "KEY_AES_128/MODE_ECB");
+ apr_pool_destroy(pool);
+
+}
+
+/**
+ * Simple test of NSS block crypt.
+ */
+static void test_crypto_block_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_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_CBC, 0, in, inlen,
+ "KEY_3DES_192/MODE_CBC");
+ /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+ /* crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_ECB, 0, in, inlen, "KEY_3DES_192/MODE_ECB"); */
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_CBC, 0, in, inlen,
+ "KEY_AES_256/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_ECB, 0, in, inlen,
+ "KEY_AES_256/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_CBC, 0, in, inlen,
+ "KEY_AES_192/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_ECB, 0, in, inlen,
+ "KEY_AES_192/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_CBC, 0, in, inlen,
+ "KEY_AES_128/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_ECB, 0, in, inlen,
+ "KEY_AES_128/MODE_ECB");
+ apr_pool_destroy(pool);
+
+}
+
+/**
+ * Encrypt NSS, decrypt OpenSSL.
+ */
+static void test_crypto_block_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_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_CBC, 0, in, inlen,
+ "KEY_3DES_192/MODE_CBC");
+
+ /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+ /* crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_ECB, 0, in, inlen, "KEY_3DES_192/MODE_ECB"); */
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_CBC, 0, in, inlen,
+ "KEY_AES_256/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_ECB, 0, in, inlen,
+ "KEY_AES_256/MODE_ECB");
+
+ /* all 4 of these tests fail to interoperate - a clue from the xml-security code is that
+ * NSS cannot distinguish between the 128 and 192 bit versions of AES. Will need to be
+ * investigated.
+ */
+ /*
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_CBC, 0, in, inlen, "KEY_AES_192/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_ECB, 0, in, inlen, "KEY_AES_192/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_CBC, 0, in, inlen, "KEY_AES_128/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_ECB, 0, in, inlen, "KEY_AES_128/MODE_ECB");
+ */
+ apr_pool_destroy(pool);
+
+}
+
+/**
+ * Encrypt OpenSSL, decrypt NSS.
+ */
+static void test_crypto_block_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_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_CBC, 0, in, inlen,
+ "KEY_3DES_192/MODE_CBC");
+
+ /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+ /* crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_ECB, 0, in, inlen, "KEY_3DES_192/MODE_ECB"); */
+
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_CBC, 0, in, inlen,
+ "KEY_AES_256/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_ECB, 0, in, inlen,
+ "KEY_AES_256/MODE_ECB");
+
+ /* all 4 of these tests fail to interoperate - a clue from the xml-security code is that
+ * NSS cannot distinguish between the 128 and 192 bit versions of AES. Will need to be
+ * investigated.
+ */
+ /*
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_CBC, 0, in, inlen, "KEY_AES_192/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_ECB, 0, in, inlen, "KEY_AES_192/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_CBC, 0, in, inlen, "KEY_AES_128/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_ECB, 0, in, inlen, "KEY_AES_128/MODE_ECB");
+ */
+ apr_pool_destroy(pool);
+
+}
+
+/**
+ * Simple test of OpenSSL block crypt.
+ */
+static void test_crypto_block_openssl_pad(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 *) TEST_STRING;
+ apr_size_t inlen = sizeof(TEST_STRING);
+
+ apr_pool_create(&pool, NULL);
+ drivers[0] = get_openssl_driver(tc, pool);
+ drivers[1] = get_openssl_driver(tc, pool);
+
+ crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_CBC, 1, in, inlen,
+ "KEY_3DES_192/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_ECB, 1, in, inlen,
+ "KEY_3DES_192/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_CBC, 1, in, inlen,
+ "KEY_AES_256/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_ECB, 1, in, inlen,
+ "KEY_AES_256/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_CBC, 1, in, inlen,
+ "KEY_AES_192/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_ECB, 1, in, inlen,
+ "KEY_AES_192/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_CBC, 1, in, inlen,
+ "KEY_AES_128/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_ECB, 1, in, inlen,
+ "KEY_AES_128/MODE_ECB");
+
+ apr_pool_destroy(pool);
+
+}
+
+/**
+ * Simple test of NSS block crypt.
+ */
+static void test_crypto_block_nss_pad(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 *) TEST_STRING;
+ apr_size_t inlen = sizeof(TEST_STRING);
+
+ apr_pool_create(&pool, NULL);
+ drivers[0] = get_nss_driver(tc, pool);
+ drivers[1] = get_nss_driver(tc, pool);
+
+ crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_CBC, 1, in, inlen,
+ "KEY_3DES_192/MODE_CBC");
+ /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+ /* crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_ECB, 1, in, inlen, "KEY_3DES_192/MODE_ECB"); */
+
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_CBC, 1, in, inlen,
+ "KEY_AES_256/MODE_CBC");
+
+ /* KEY_AES_256 / MODE_ECB doesn't support padding on NSS */
+ /*crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_ECB, 1, in, inlen, "KEY_AES_256/MODE_ECB");*/
+
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_CBC, 1, in, inlen,
+ "KEY_AES_192/MODE_CBC");
+
+ /* KEY_AES_256 / MODE_ECB doesn't support padding on NSS */
+ /*crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_ECB, 1, in, inlen, "KEY_AES_192/MODE_ECB");*/
+
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_CBC, 1, in, inlen,
+ "KEY_AES_128/MODE_CBC");
+
+ /* KEY_AES_256 / MODE_ECB doesn't support padding on NSS */
+ /*crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_ECB, 1, in, inlen, "KEY_AES_128/MODE_ECB");*/
+
+ apr_pool_destroy(pool);
+
+}
+
+/**
+ * Encrypt NSS, decrypt OpenSSL.
+ */
+static void test_crypto_block_nss_openssl_pad(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 *) TEST_STRING;
+ apr_size_t inlen = sizeof(TEST_STRING);
+
+ apr_pool_create(&pool, NULL);
+ drivers[0] = get_nss_driver(tc, pool);
+ drivers[1] = get_openssl_driver(tc, pool);
+
+ crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_CBC, 1, in, inlen,
+ "KEY_3DES_192/MODE_CBC");
+
+ /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+ /* crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_ECB, 1, in, inlen, "KEY_3DES_192/MODE_ECB"); */
+
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_CBC, 1, in, inlen,
+ "KEY_AES_256/MODE_CBC");
+
+ /* KEY_AES_256 / MODE_ECB doesn't support padding on NSS */
+ /*crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_ECB, 1, in, inlen, "KEY_AES_256/MODE_ECB");*/
+
+ /* all 4 of these tests fail to interoperate - a clue from the xml-security code is that
+ * NSS cannot distinguish between the 128 and 192 bit versions of AES. Will need to be
+ * investigated.
+ */
+ /*
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_CBC, 1, in, inlen, "KEY_AES_192/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_ECB, 1, in, inlen, "KEY_AES_192/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_CBC, 1, in, inlen, "KEY_AES_128/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_ECB, 1, in, inlen, "KEY_AES_128/MODE_ECB");
+ */
+ apr_pool_destroy(pool);
+
+}
+
+/**
+ * Encrypt OpenSSL, decrypt NSS.
+ */
+static void test_crypto_block_openssl_nss_pad(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 *) TEST_STRING;
+ apr_size_t inlen = sizeof(TEST_STRING);
+
+ apr_pool_create(&pool, NULL);
+ drivers[0] = get_openssl_driver(tc, pool);
+ drivers[1] = get_nss_driver(tc, pool);
+ crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_CBC, 1, in, inlen,
+ "KEY_3DES_192/MODE_CBC");
+
+ /* KEY_3DES_192 / MODE_ECB doesn't work on NSS */
+ /* crypto_block_cross(tc, pool, drivers, KEY_3DES_192, MODE_ECB, 1, in, inlen, "KEY_3DES_192/MODE_ECB"); */
+
+ crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_CBC, 1, in, inlen,
+ "KEY_AES_256/MODE_CBC");
+
+ /* KEY_AES_256 / MODE_ECB doesn't support padding on NSS */
+ /*crypto_block_cross(tc, pool, drivers, KEY_AES_256, MODE_ECB, 1, in, inlen, "KEY_AES_256/MODE_ECB");*/
+
+ /* all 4 of these tests fail to interoperate - a clue from the xml-security code is that
+ * NSS cannot distinguish between the 128 and 192 bit versions of AES. Will need to be
+ * investigated.
+ */
+ /*
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_CBC, 1, in, inlen, "KEY_AES_192/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_192, MODE_ECB, 1, in, inlen, "KEY_AES_192/MODE_ECB");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_CBC, 1, in, inlen, "KEY_AES_128/MODE_CBC");
+ crypto_block_cross(tc, pool, drivers, KEY_AES_128, MODE_ECB, 1, in, inlen, "KEY_AES_128/MODE_ECB");
+ */
+ apr_pool_destroy(pool);
+
+}
+
+abts_suite *testcrypto(abts_suite *suite) {
+ suite = ADD_SUITE(suite);
+
+ /* test simple init and shutdown */
+ abts_run_test(suite, test_crypto_init, NULL);
+
+ /* test a simple encrypt / decrypt operation - openssl */
+ abts_run_test(suite, test_crypto_block_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 padded encrypt / decrypt operation - nss */
+ abts_run_test(suite, test_crypto_block_nss_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 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);
+
+ return suite;
+}
+
+#else
+
+/**
+ * Dummy test suite when crypto is turned off.
+ */
+abts_suite *testcrypto(abts_suite *suite)
+{
+ return ADD_SUITE(suite);
+}
+
+#endif /* APU_HAVE_CRYPTO */
diff --git a/test/testutil.h b/test/testutil.h
index 6b8e5f84..07b34a84 100644
--- a/test/testutil.h
+++ b/test/testutil.h
@@ -50,6 +50,7 @@ abts_suite *testbuckets(abts_suite *suite);
abts_suite *testpass(abts_suite *suite);
abts_suite *testmd4(abts_suite *suite);
abts_suite *testmd5(abts_suite *suite);
+abts_suite *testcrypto(abts_suite *suite);
abts_suite *testldap(abts_suite *suite);
abts_suite *testdbd(abts_suite *suite);
abts_suite *testdate(abts_suite *suite);