From: Pekka Riikonen Date: Mon, 9 Jul 2007 17:33:41 +0000 (+0000) Subject: Added SILC Accelerator Library. X-Git-Tag: 1.2.beta1~197 X-Git-Url: http://git.silcnet.org/gitweb/?a=commitdiff_plain;h=1218ffebfc22091283bbd73886c8e7cd734e856f;p=crypto.git Added SILC Accelerator Library. Added software accelerator. --- diff --git a/lib/silcacc/Makefile.ad b/lib/silcacc/Makefile.ad new file mode 100644 index 00000000..3bb430aa --- /dev/null +++ b/lib/silcacc/Makefile.ad @@ -0,0 +1,30 @@ +# +# Makefile.ad +# +# Author: Pekka Riikonen +# +# Copyright (C) 2007 Pekka Riikonen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign + +noinst_LTLIBRARIES = libsilcacc.la + +libsilcacc_la_SOURCES = silcacc.c silcacc_pkcs.c softacc.c + +#ifdef SILC_DIST_TOOLKIT +include_HEADERS = silcacc.h +#endif SILC_DIST_TOOLKIT + +EXTRA_DIST = *.h $(SILC_EXTRA_DIST) + +include $(top_srcdir)/Makefile.defines.in diff --git a/lib/silcacc/silcacc.c b/lib/silcacc/silcacc.c new file mode 100644 index 00000000..e50a0eae --- /dev/null +++ b/lib/silcacc/silcacc.c @@ -0,0 +1,174 @@ +/* + + silcacc.c + + Author: Pekka Riikonen + + Copyright (C) 2007 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#include "silc.h" +#include "softacc.h" + +/************************** Types and definitions ***************************/ + +#ifndef SILC_SYMBIAN +/* Dynamically registered list of accelerators. */ +SilcDList silc_acc_list = NULL; +#endif /* SILC_SYMBIAN */ + +/* Static list of accelerators */ +const SilcAcceleratorStruct *silc_default_accs[] = +{ +#ifndef SILC_SYMBIAN + /* Software accelerator */ + &softacc, +#endif /* SILC_SYMBIAN */ + NULL +}; + +/*************************** SILC Accelerator API ***************************/ + +/* Register accelerator */ + +SilcBool silc_acc_register(const SilcAccelerator acc) +{ + if (!acc) + return FALSE; + + if (!silc_acc_list) { + silc_acc_list = silc_dlist_init(); + if (!silc_acc_list) + return FALSE; + } + + SILC_LOG_DEBUG(("Register accelerator %p, name %s", acc, acc->name)); + silc_dlist_add(silc_acc_list, acc); + + return TRUE; +} + +/* Unregister accelerator */ + +void silc_acc_unregister(SilcAccelerator acc) +{ + if (!acc) + return; + + if (!silc_acc_list) + return; + + SILC_LOG_DEBUG(("Unregister accelerator %p, name %s", acc, acc->name)); + silc_dlist_del(silc_acc_list, acc); + + if (!silc_dlist_count(silc_acc_list)) { + silc_dlist_uninit(silc_acc_list); + silc_acc_list = NULL; + } +} + +/* Initialize accelerator */ + +SilcBool silc_acc_init(SilcAccelerator acc, SilcSchedule schedule, ...) +{ + va_list va; + SilcBool ret; + + if (!acc || !schedule) + return FALSE; + + SILC_LOG_DEBUG(("Initialize accelerator %p, name %s", acc, acc->name)); + + va_start(va, schedule); + ret = acc->init(schedule, va); + va_end(va); + + return ret; +} + +/* Uninitialize accelerator */ + +SilcBool silc_acc_uninit(SilcAccelerator acc) +{ + if (!acc) + return FALSE; + + SILC_LOG_DEBUG(("Uninitialize accelerator %p, name %s", acc, acc->name)); + return acc->uninit(); +} + +/* Get list of registered accelerator */ + +SilcDList silc_acc_get_supported(void) +{ + SilcDList list; + SilcAccelerator acc; + int i; + + list = silc_dlist_init(); + if (!list) + return NULL; + + if (silc_acc_list) { + silc_dlist_start(silc_acc_list); + while ((acc = silc_dlist_get(silc_acc_list))) + silc_dlist_add(list, acc); + } + + for (i = 0; silc_default_accs[i]->name; i++) + silc_dlist_add(list, (void *)silc_default_accs[i]); + + return list; +} + +/* Get accelerator */ + +SilcAccelerator silc_acc_find(const char *name) +{ + SilcAccelerator acc; + int i; + + if (!name) + return NULL; + + SILC_LOG_DEBUG(("Find accelerator %s", name)); + + if (silc_acc_list) { + silc_dlist_start(silc_acc_list); + while ((acc = silc_dlist_get(silc_acc_list))) { + if (!strcmp(acc->name, name)) { + SILC_LOG_DEBUG(("Found accelerator %p", acc)); + return acc; + } + } + } + + for (i = 0; silc_default_accs[i]->name; i++) { + if (!strcmp(silc_default_accs[i]->name, name)) { + SILC_LOG_DEBUG(("Found accelerator %p", silc_default_accs[i])); + return (SilcAccelerator)silc_default_accs[i]; + } + } + + SILC_LOG_DEBUG(("Accelerator %s does not exist", name)); + return NULL; +} + +/* Get accelerator name */ + +const char *silc_acc_get_name(SilcAccelerator acc) +{ + if (!acc) + return NULL; + return acc->name; +} diff --git a/lib/silcacc/silcacc.h b/lib/silcacc/silcacc.h new file mode 100644 index 00000000..e438bc8c --- /dev/null +++ b/lib/silcacc/silcacc.h @@ -0,0 +1,252 @@ +/* + + silcacc.h + + Author: Pekka Riikonen + + Copyright (C) 2007 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +/****h* silcskr/SILC Accelerator Interface + * + * DESCRIPTION + * + ***/ + +#ifndef SILCACC_H +#define SILCACC_H + +/****s* silcacc/SilcAccAPI/SilcAccelerator + * + * NAME + * + * typedef struct SilcAcceleratorObject { ... } + * *SilcAccelerator, SilcAcceleratorStruct; + * + * DESCRIPTION + * + * The accelerator context. This is given as argument to silc_acc_register + * when registering new accelerator, and it is given as argument to all + * other silc_acc_* functions. Registered accelerator context can be + * retrieved by calling silc_acc_find. + * + ***/ +typedef struct SilcAcceleratorObject { + const char *name; /* Accelerator's name */ + SilcBool (*init)(SilcSchedule schedule, + va_list va); /* Initialize accelerator */ + SilcBool (*uninit)(void); /* Uninitialize accelerator */ + const SilcPKCSAlgorithm *pkcs; /* Accelerated PKCS algorithms */ +#if 0 + const SilcDHObject *dh; /* Accelerated Diffie-Hellmans */ + const SilcCipherObject *cipher; /* Accelerated ciphers */ + const SilcHashObject *hash; /* Accelerated hashes */ + const SilcHmacObject *hmac; /* Accelerated HMACs */ + const SilcRngObject *rng; /* Accelerated RNG's */ +#endif /* 0 */ +} *SilcAccelerator, SilcAcceleratorStruct; + +/****f* silcacc/SilcAccAPI/silc_acc_register + * + * SYNOPSIS + * + * SilcBool silc_acc_register(const SilcAccelerator acc); + * + * DESCRIPTION + * + * Register new accelerator to the accelerator library. The `acc' + * is the accelerator context to be registered. + * + * NOTES + * + * This needs to be called only when adding new accelerator to the + * library. The accelerator library has some pre-registered accelerators + * that need not be registered with this call. + * + ***/ +SilcBool silc_acc_register(const SilcAccelerator acc); + +/****f* silcacc/SilcAccAPI/silc_acc_unregister + * + * SYNOPSIS + * + * void silc_acc_unregister(SilcAccelerator acc); + * + * DESCRIPTION + * + * Unregister the accelerator `acc' from the accelerator library. The + * accelerator cannot be used anymore after this call has returned. + * + ***/ +void silc_acc_unregister(SilcAccelerator acc); + +/****f* silcacc/SilcAccAPI/silc_acc_init + * + * SYNOPSIS + * + * SilcBool silc_acc_init(SilcAccelerator acc, SilcSchedule schedule, ...); + * + * DESCRIPTION + * + * Initialize accelerator `acc'. Usually accelerator may be initialized + * only once and should be done after registering it. The `schedule' + * must be given as argument, in case the accelerator needs to do operations + * through the scheduler. The variable argument list is optional + * accelerator specific initialization arguments. The argument list must + * be ended with NULL. Returns FALSE if initialization failed. + * + * EXAMPLE + * + * silc_acc_init(softacc, "min_threads", 2, "max_threads", 16, NULL); + * + ***/ +SilcBool silc_acc_init(SilcAccelerator acc, SilcSchedule schedule, ...); + +/****f* silcacc/SilcAccAPI/silc_acc_uninit + * + * SYNOPSIS + * + * SilcBool silc_acc_uninit(SilcAccelerator acc); + * + * DESCRIPTION + * + * Uninitialize the accelerator `acc'. The accelerator may not be used + * after this call has returned. Some accelerators may be re-initialized + * by calling silc_acc_init again. Returns FALSE if error occurred + * during uninitializing. + * + ***/ +SilcBool silc_acc_uninit(SilcAccelerator acc); + +/****f* silcacc/SilcAccAPI/silc_acc_get_supported + * + * SYNOPSIS + * + * SilcDList silc_acc_get_supported(void); + * + * DESCRIPTION + * + * Returns list of registered accelerators. The caller must free the + * returned list by calling silc_dlist_uninit. + * + ***/ +SilcDList silc_acc_get_supported(void); + +/****f* silcacc/SilcAccAPI/silc_acc_find + * + * SYNOPSIS + * + * SilcAccelerator silc_acc_find(const char *name); + * + * DESCRIPTION + * + * Find accelerator by its name indicated by `name'. Returns the + * accelerator context or NULL if such accelerator is not registered. + * + ***/ +SilcAccelerator silc_acc_find(const char *name); + +/****f* silcacc/SilcAccAPI/silc_acc_get_name + * + * SYNOPSIS + * + * const char *silc_acc_get_name(SilcAccelerator acc); + * + * DESCRIPTION + * + * Returns the name of the accelerator `acc'. + * + ***/ +const char *silc_acc_get_name(SilcAccelerator acc); + +/****f* silcacc/SilcAccAPI/silc_acc_public_key + * + * SYNOPSIS + * + * SilcPublicKey silc_acc_public_key(SilcAccelerator acc, + * SilcPublicKey public_key); + * + * DESCRIPTION + * + * Accelerate the public key indicated by `public_key'. Returns new + * accelerated SilcPublicKey context. It can be used just as normal + * public key and must be freed by calling silc_pkcs_public_key_free. + * The associated `public_key' is not freed when the accelerated public + * key is freed. The `public_key' must not be freed as long as it is + * accelerated. + * + * The associated `public_key' can be retrieved from the returned + * public key by calling silc_acc_get_public_key. + * + ***/ +SilcPublicKey silc_acc_public_key(SilcAccelerator acc, + SilcPublicKey public_key); + +/****f* silcacc/SilcAccAPI/silc_acc_private_key + * + * SYNOPSIS + * + * SilcPrivateKey silc_acc_private_key(SilcAccelerator acc, + * SilcPrivateKey private_key); + * + * DESCRIPTION + * + * Accelerate the private key indicated by `private_key'. Returns new + * accelerated SilcPrivateKey context. It can be used just as normal + * private key and must be freed by calling silc_pkcs_private_key_free. + * The associated `private_key' is not freed when the accelerated private + * key is freed. The `private_key' must not be freed as long as it is + * accelerated. + * + * The associated `private_key' can be retrieved from the returned + * private key by calling silc_acc_get_private_key. + * + ***/ +SilcPrivateKey silc_acc_private_key(SilcAccelerator acc, + SilcPrivateKey private_key); + +/****f* silcacc/SilcAccAPI/silc_acc_get_public_key + * + * SYNOPSIS + * + * SilcPublicKey silc_acc_get_public_key(SilcAccelerator acc, + * SilcPublicKey public_key); + * + * DESCRIPTION + * + * Returns the underlaying public key from the accelerated public key + * indicated by `public_key'. Returns NULL if `public_key' is not + * accelerated public key. + * + ***/ +SilcPublicKey silc_acc_get_public_key(SilcAccelerator acc, + SilcPublicKey public_key); + +/****f* silcacc/SilcAccAPI/silc_acc_get_private_key + * + * SYNOPSIS + * + * SilcPrivateKey silc_acc_get_private_key(SilcAccelerator acc, + * SilcPrivateKey private_key); + * + * DESCRIPTION + * + * Returns the underlaying private key from the accelerated private key + * indicated by `private_key'. Returns NULL if `private_key' is not + * accelerated private key. + * + ***/ +SilcPrivateKey silc_acc_get_private_key(SilcAccelerator acc, + SilcPrivateKey private_key); + +#endif /* SILCACC_H */ diff --git a/lib/silcacc/silcacc_pkcs.c b/lib/silcacc/silcacc_pkcs.c new file mode 100644 index 00000000..ee2c5a22 --- /dev/null +++ b/lib/silcacc/silcacc_pkcs.c @@ -0,0 +1,426 @@ +/* + + silcacc_pkcs.c + + Author: Pekka Riikonen + + Copyright (C) 2007 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#include "silc.h" + +/************************** Types and definitions ***************************/ + +SILC_PKCS_GET_ALGORITHM(silc_acc_pkcs_get_algorithm); +SILC_PKCS_IMPORT_PUBLIC_KEY_FILE(silc_acc_pkcs_import_public_key_file); +SILC_PKCS_IMPORT_PUBLIC_KEY(silc_acc_pkcs_import_public_key); +SILC_PKCS_EXPORT_PUBLIC_KEY_FILE(silc_acc_pkcs_export_public_key_file); +SILC_PKCS_EXPORT_PUBLIC_KEY(silc_acc_pkcs_export_public_key); +SILC_PKCS_PUBLIC_KEY_BITLEN(silc_acc_pkcs_public_key_bitlen); +SILC_PKCS_PUBLIC_KEY_COPY(silc_acc_pkcs_public_key_copy); +SILC_PKCS_PUBLIC_KEY_COMPARE(silc_acc_pkcs_public_key_compare); +SILC_PKCS_PUBLIC_KEY_FREE(silc_acc_pkcs_public_key_free); +SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_acc_pkcs_import_private_key_file); +SILC_PKCS_IMPORT_PRIVATE_KEY(silc_acc_pkcs_import_private_key); +SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_acc_pkcs_export_private_key_file); +SILC_PKCS_EXPORT_PRIVATE_KEY(silc_acc_pkcs_export_private_key); +SILC_PKCS_PRIVATE_KEY_BITLEN(silc_acc_pkcs_private_key_bitlen); +SILC_PKCS_PRIVATE_KEY_FREE(silc_acc_pkcs_private_key_free); +SILC_PKCS_ENCRYPT(silc_acc_pkcs_encrypt); +SILC_PKCS_DECRYPT(silc_acc_pkcs_decrypt); +SILC_PKCS_SIGN(silc_acc_pkcs_sign); +SILC_PKCS_VERIFY(silc_acc_pkcs_verify); + +/* Accelerator public key */ +typedef struct { + int pkcs_index; /* Accelerator PKCS index */ + SilcAccelerator acc; /* The accelerator */ + void *context; /* Accelerator context */ + SilcPublicKey accelerated; /* Associated public key */ +} *SilcAcceleratorPublicKey; + +/* Accelerator private key */ +typedef struct { + int pkcs_index; /* Accelerator PKCS index */ + SilcAccelerator acc; /* The accelerator */ + void *context; /* Accelerator context */ + SilcPrivateKey accelerated; /* Associated private key */ +} *SilcAcceleratorPrivateKey; + +/*************************** Accelerator PKCS API ***************************/ + +/* The PKCS API for the accelerated public key and private key is simply + a wrapper for the underlaying key. Encrypt, decrypt, sign and verify + operations are accelerated by calling the accelerator operations. */ + +const SilcPKCSObject silc_acc_pkcs = +{ + SILC_PKCS_SILC, + + /* Wrappers */ + silc_acc_pkcs_get_algorithm, + silc_acc_pkcs_import_public_key_file, + silc_acc_pkcs_import_public_key, + silc_acc_pkcs_export_public_key_file, + silc_acc_pkcs_export_public_key, + silc_acc_pkcs_public_key_bitlen, + silc_acc_pkcs_public_key_copy, + silc_acc_pkcs_public_key_compare, + silc_acc_pkcs_public_key_free, + silc_acc_pkcs_import_private_key_file, + silc_acc_pkcs_import_private_key, + silc_acc_pkcs_export_private_key_file, + silc_acc_pkcs_export_private_key, + silc_acc_pkcs_private_key_bitlen, + silc_acc_pkcs_private_key_free, + + /* Accelerated */ + silc_acc_pkcs_encrypt, + silc_acc_pkcs_decrypt, + silc_acc_pkcs_sign, + silc_acc_pkcs_verify +}; + +SILC_PKCS_GET_ALGORITHM(silc_acc_pkcs_get_algorithm) +{ + SilcAcceleratorPublicKey pub = public_key; + return pub->accelerated->pkcs->get_algorithm(pub->accelerated->pkcs, + pub->accelerated->public_key); +} + +SILC_PKCS_IMPORT_PUBLIC_KEY_FILE(silc_acc_pkcs_import_public_key_file) +{ + /* Not implemented */ + return FALSE; +} + +SILC_PKCS_IMPORT_PUBLIC_KEY(silc_acc_pkcs_import_public_key) +{ + /* Not implemented */ + return FALSE; +} + +SILC_PKCS_EXPORT_PUBLIC_KEY_FILE(silc_acc_pkcs_export_public_key_file) +{ + SilcAcceleratorPublicKey pub = public_key; + return pub->accelerated->pkcs-> + export_public_key_file(pub->accelerated->pkcs, NULL, + pub->accelerated->public_key, + encoding, ret_len); +} + +SILC_PKCS_EXPORT_PUBLIC_KEY(silc_acc_pkcs_export_public_key) +{ + SilcAcceleratorPublicKey pub = public_key; + return pub->accelerated->pkcs->export_public_key(pub->accelerated->pkcs, + NULL, + pub->accelerated->public_key, + ret_len); +} + +SILC_PKCS_PUBLIC_KEY_BITLEN(silc_acc_pkcs_public_key_bitlen) +{ + SilcAcceleratorPublicKey pub = public_key; + return pub->accelerated->pkcs-> + public_key_bitlen(pub->accelerated->pkcs, + pub->accelerated->public_key); +} + +SILC_PKCS_PUBLIC_KEY_COPY(silc_acc_pkcs_public_key_copy) +{ + SilcAcceleratorPublicKey pub = public_key; + return pub->accelerated->pkcs->public_key_copy(pub->accelerated->pkcs, + pub->accelerated->public_key); +} + +SILC_PKCS_PUBLIC_KEY_COMPARE(silc_acc_pkcs_public_key_compare) +{ + /* XXX */ + return FALSE; +} + +SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_acc_pkcs_import_private_key_file) +{ + return 0; +} + +SILC_PKCS_IMPORT_PRIVATE_KEY(silc_acc_pkcs_import_private_key) +{ + return 0; +} + +SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_acc_pkcs_export_private_key_file) +{ + return 0; +} + +SILC_PKCS_EXPORT_PRIVATE_KEY(silc_acc_pkcs_export_private_key) +{ + return 0; +} + +SILC_PKCS_PRIVATE_KEY_BITLEN(silc_acc_pkcs_private_key_bitlen) +{ + return 0; +} + +SILC_PKCS_PUBLIC_KEY_FREE(silc_acc_pkcs_public_key_free) +{ + +} + +SILC_PKCS_PRIVATE_KEY_FREE(silc_acc_pkcs_private_key_free) +{ + +} + +SILC_PKCS_ENCRYPT(silc_acc_pkcs_encrypt) +{ + SilcAcceleratorPublicKey pub = public_key; + + /* Accelerate */ + return pub->acc->pkcs[pub->pkcs_index].encrypt( + &pub->acc->pkcs[pub->pkcs_index], pub->context, src, + src_len, rng, encrypt_cb, context); +} + +SILC_PKCS_DECRYPT(silc_acc_pkcs_decrypt) +{ + SilcAcceleratorPrivateKey prv = private_key; + + /* Accelerate */ + return prv->acc->pkcs[prv->pkcs_index].decrypt( + &prv->acc->pkcs[prv->pkcs_index], prv->context, src, + src_len, decrypt_cb, context); +} + +SILC_PKCS_SIGN(silc_acc_pkcs_sign) +{ + SilcAcceleratorPrivateKey prv = private_key; + + /* Accelerate */ + return prv->acc->pkcs[prv->pkcs_index].sign( + &prv->acc->pkcs[prv->pkcs_index], prv->context, src, + src_len, compute_hash, hash, sign_cb, context); +} + +SILC_PKCS_VERIFY(silc_acc_pkcs_verify) +{ + SilcAcceleratorPublicKey pub = public_key; + + /* Accelerate */ + return pub->acc->pkcs[pub->pkcs_index].verify( + &pub->acc->pkcs[pub->pkcs_index], pub->context, + signature, signature_len, data, data_len, hash, + verify_cb, context); +} + +/*************************** SILC Accelerator API ***************************/ + +/* Accelerate public key */ + +SilcPublicKey silc_acc_public_key(SilcAccelerator acc, + SilcPublicKey public_key) +{ + SilcPublicKey pubkey; + SilcAcceleratorPublicKey acc_pubkey; + const SilcPKCSAlgorithm *alg; + int i; + + if (!acc || !public_key) + return NULL; + + SILC_LOG_DEBUG(("Accelerate public key %p with accelerator %s", + public_key, acc->name)); + + if (!acc->pkcs) { + SILC_LOG_ERROR(("Accelerator '%s' does not support public key " + "acceleration", acc->name)); + return NULL; + } + + /* Check that accelerator supports this public key algorithm */ + alg = silc_pkcs_get_algorithm(public_key); + if (!alg) + return NULL; + for (i = 0; acc->pkcs[i].name; i++) { + if ((!strcmp(acc->pkcs[i].name, alg->name) && + !strcmp(acc->pkcs[i].scheme, alg->scheme)) || + !strcmp(acc->pkcs[i].name, "any")) { + alg = NULL; + break; + } + } + if (alg) { + SILC_LOG_DEBUG(("Accelerator %s does not support %s/%s acceleration", + alg->name, alg->scheme)); + return NULL; + } + + pubkey = silc_calloc(1, sizeof(*pubkey)); + if (!pubkey) + return NULL; + + /* Allocate PKCS operations */ + pubkey->pkcs = silc_calloc(1, sizeof(*pubkey->pkcs)); + if (!pubkey->pkcs) { + silc_free(pubkey); + return NULL; + } + *pubkey->pkcs = silc_acc_pkcs; + pubkey->pkcs->type = silc_pkcs_get_type(public_key); + + /* Allocate accelerator public key */ + acc_pubkey = silc_calloc(1, sizeof(*acc_pubkey)); + if (!acc_pubkey) { + silc_free(pubkey->pkcs); + silc_free(pubkey); + return NULL; + } + acc_pubkey->accelerated = public_key; + acc_pubkey->acc = acc; + acc_pubkey->pkcs_index = i; + + /* Accelerate the public key. Returns accelerator context. */ + if (!acc->pkcs->import_public_key(&acc->pkcs[i], public_key, 0, + &acc_pubkey->context)) { + SILC_LOG_ERROR(("Error accelerating public key with accelerator '%s'", + acc->name)); + silc_free(acc_pubkey); + silc_free(pubkey->pkcs); + silc_free(pubkey); + return NULL; + } + pubkey->public_key = acc_pubkey; + + SILC_LOG_DEBUG(("New accelerated public key %p", pubkey)); + + return pubkey; +} + +/* Accelerate private key */ + +SilcPrivateKey silc_acc_private_key(SilcAccelerator acc, + SilcPrivateKey private_key) +{ + SilcPrivateKey privkey; + SilcAcceleratorPrivateKey acc_privkey; + const SilcPKCSAlgorithm *alg; + int i; + + if (!acc || !private_key) + return NULL; + + SILC_LOG_DEBUG(("Accelerate private key %p with accelerator %s", + private_key, acc->name)); + + if (!acc->pkcs) { + SILC_LOG_ERROR(("Accelerator '%s' does not support private key " + "acceleration", acc->name)); + return NULL; + } + + /* Check that accelerator supports this private key algorithm */ + alg = silc_pkcs_get_algorithm(private_key); + if (!alg) + return NULL; + for (i = 0; acc->pkcs[i].name; i++) { + if ((!strcmp(acc->pkcs[i].name, alg->name) && + !strcmp(acc->pkcs[i].scheme, alg->scheme)) || + !strcmp(acc->pkcs[i].name, "any")) { + alg = NULL; + break; + } + } + if (alg) { + SILC_LOG_DEBUG(("Accelerator %s does not support %s/%s acceleration", + alg->name, alg->scheme)); + return NULL; + } + + privkey = silc_calloc(1, sizeof(*privkey)); + if (!privkey) + return NULL; + + /* Allocate PKCS operations */ + privkey->pkcs = silc_calloc(1, sizeof(*privkey->pkcs)); + if (!privkey->pkcs) { + silc_free(privkey); + return NULL; + } + *privkey->pkcs = silc_acc_pkcs; + privkey->pkcs->type = silc_pkcs_get_type(private_key); + + /* Allocate accelerator public key */ + acc_privkey = silc_calloc(1, sizeof(*acc_privkey)); + if (!acc_privkey) { + silc_free(privkey->pkcs); + silc_free(privkey); + return NULL; + } + acc_privkey->accelerated = private_key; + acc_privkey->acc = acc; + acc_privkey->pkcs_index = i; + + /* Accelerate the public key. Returns accelerator context. */ + if (!acc->pkcs->import_private_key(&acc->pkcs[i], private_key, 0, + &acc_privkey->context)) { + SILC_LOG_ERROR(("Error accelerating private key with accelerator '%s'", + acc->name)); + silc_free(acc_privkey); + silc_free(privkey->pkcs); + silc_free(privkey); + return NULL; + } + privkey->private_key = acc_privkey; + + SILC_LOG_DEBUG(("New accelerated private key %p", privkey)); + + return privkey; +} + +/* Get associated public key */ + +SilcPublicKey silc_acc_get_public_key(SilcAccelerator acc, + SilcPublicKey public_key) +{ + SilcAcceleratorPublicKey pubkey; + + if (!public_key) + return NULL; + + if (public_key->pkcs->get_algorithm != silc_acc_pkcs_get_algorithm) + return NULL; + + pubkey = public_key->public_key; + + return pubkey->accelerated; +} + +/* Get associated private key */ + +SilcPrivateKey silc_acc_get_private_key(SilcAccelerator acc, + SilcPrivateKey private_key) +{ + SilcAcceleratorPrivateKey privkey; + + if (!private_key) + return NULL; + + if (private_key->pkcs->get_algorithm != silc_acc_pkcs_get_algorithm) + return NULL; + + privkey = private_key->private_key; + + return privkey->accelerated; +} diff --git a/lib/silcacc/softacc.c b/lib/silcacc/softacc.c new file mode 100644 index 00000000..89c1adaf --- /dev/null +++ b/lib/silcacc/softacc.c @@ -0,0 +1,535 @@ +/* + + softacc.c + + Author: Pekka Riikonen + + Copyright (C) 2007 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#include "silc.h" +#include "softacc.h" + +/* Software accelerator is a thread-pool system where public key and private + key operations are executed in threads for the purpose of off-loading and + balancing the computations across multiple processors. */ + +#define SILC_SOFTACC_MIN_THREADS 0 +#define SILC_SOFTACC_MAX_THREADS 4 + +/************************** Types and definitions ***************************/ + +/* Software accelerator PKCS algorithm operations */ +const SilcPKCSAlgorithm softacc_pkcs[] = +{ + { + "any", "any", NULL, NULL, + silc_softacc_acc_public_key, + NULL, NULL, NULL, NULL, + silc_softacc_free_public_key, + silc_softacc_acc_private_key, + NULL, NULL, + silc_softacc_free_private_key, + silc_softacc_encrypt, + silc_softacc_decrypt, + silc_softacc_sign, + silc_softacc_verify, + }, + + { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL + } +}; + +/* Software accelerator operations */ +const SilcAcceleratorStruct softacc = +{ + "softacc", silc_softacc_init, silc_softacc_uninit, softacc_pkcs +}; + +/* Software accelerator public key */ +typedef struct { + SilcPublicKey key; /* Accelerated public key */ +} *SilcSoftaccPublicKey; + +/* Software accelerator private key */ +typedef struct { + SilcPrivateKey key; /* Accelerated private key */ +} *SilcSoftaccPrivateKey; + +/* Execution types */ +typedef enum { + SILC_SOFTACC_ENCRYPT, + SILC_SOFTACC_DECRYPT, + SILC_SOFTACC_SIGN, + SILC_SOFTACC_VERIFY, +} SilcSoftaccType; + +/* Executor context */ +typedef struct { + SilcStack stack; /* Executor stack */ + void *context; /* Callback context */ + SilcSoftaccType type; /* Execution type */ + SilcAsyncOperationStruct op; /* Operation for aborting */ + + unsigned char *src; /* Source data */ + unsigned char *data; /* More source data */ + SilcUInt32 src_len; + SilcUInt32 data_len; + SilcHash hash; /* Hash function to use */ + SilcRng rng; /* RNG, may be NULL */ + + union { + SilcPublicKey public_key; + SilcPrivateKey private_key; + } key; + + union { + SilcPKCSEncryptCb encrypt_cb; + SilcPKCSDecryptCb decrypt_cb; + SilcPKCSSignCb sign_cb; + SilcPKCSVerifyCb verify_cb; + } cb; + + unsigned char *result_data; + SilcUInt32 result_len; + + unsigned int result : 1; + unsigned int compute_hash : 1; + unsigned int aborted : 1; +} *SilcSoftaccExec; + +/* Software accelerator context */ +typedef struct { + SilcSchedule schedule; /* Scheduler */ + SilcThreadPool tp; /* The thread pool */ +} *SilcSoftacc; + +SilcSoftacc sa = NULL; /* The accelerator */ + +/***************************** Accelerator API ******************************/ + +/* Initialize software accelerator. Optional initialization parameters: + + min_threads number Minimum number of threads (default 0) + max_thread number Maximum number of threads (default 4) + + Eg. silc_acc_init(softacc, "min_threads", 2, "max_threads", 8, NULL); + +*/ + +SilcBool silc_softacc_init(SilcSchedule schedule, va_list va) +{ + SilcUInt32 min_threads = SILC_SOFTACC_MIN_THREADS; + SilcUInt32 max_threads = SILC_SOFTACC_MAX_THREADS; + char *opt; + + if (!schedule) + return FALSE; + + /* If already initialized, uninitialize first. */ + if (sa) + silc_softacc_uninit(); + + /* Get options */ + while ((opt = va_arg(va, char *))) { + if (!strcmp(opt, "min_threads")) + min_threads = va_arg(va, SilcUInt32); + else if (!strcmp(opt, "max_threads")) + max_threads = va_arg(va, SilcUInt32); + } + + SILC_LOG_DEBUG(("Initialize software accelerator, min_threads %d, " + "max_threads %d", min_threads, max_threads)); + + sa = silc_calloc(1, sizeof(*sa)); + if (!sa) + return FALSE; + + sa->schedule = schedule; + + /* Start the thread pool */ + sa->tp = silc_thread_pool_alloc(NULL, min_threads, max_threads, TRUE); + if (!sa->tp) { + silc_free(sa); + return FALSE; + } + + return TRUE; +} + +/* Uninitialize */ + +SilcBool silc_softacc_uninit(void) +{ + if (!sa) + return FALSE; + + SILC_LOG_DEBUG(("Uninitialize software accelerator")); + + silc_thread_pool_free(sa->tp, TRUE); + silc_free(sa); + sa = NULL; + + return TRUE; +} + +/****************************** PKCS ALG API ********************************/ + +/* Abort operation */ + +void silc_softacc_abort(SilcAsyncOperation op, void *context) +{ + SilcSoftaccExec e = context; + e->aborted = TRUE; +} + +/* Accelerator completion, executed in main thread. */ + +SILC_TASK_CALLBACK(silc_softacc_completion) +{ + SilcSoftaccExec e = context; + SilcStack stack = e->stack; + + /* At the latest, abort is catched here in the main thread. Don't + deliver callback if we were aborted */ + if (e->aborted) + goto out; + + SILC_LOG_DEBUG(("Call completion, result=%s", e->result ? "Ok" : "failed")); + + /* Call completion callback */ + switch (e->type) { + case SILC_SOFTACC_ENCRYPT: + e->cb.encrypt_cb(e->result, e->result_data, e->result_len, e->context); + break; + + case SILC_SOFTACC_DECRYPT: + e->cb.decrypt_cb(e->result, e->result_data, e->result_len, e->context); + break; + + case SILC_SOFTACC_SIGN: + e->cb.sign_cb(e->result, e->result_data, e->result_len, e->context); + break; + + case SILC_SOFTACC_VERIFY: + e->cb.verify_cb(e->result, e->context); + break; + } + + out: + silc_sfree(stack, e->src); + silc_sfree(stack, e->data); + silc_sfree(stack, e); + silc_stack_free(stack); +} + +/* Callback for encrypt, decrypt and signature */ + +void silc_softacc_data_cb(SilcBool success, const unsigned char *data, + SilcUInt32 data_len, void *context) +{ + SilcSoftaccExec e = context; + SilcStack stack = e->stack; + + /* Pop e->src and e->data from memory */ + silc_stack_pop(stack); + + if (success) + e->result_data = silc_smemdup(stack, data, data_len); + e->result_len = data_len; + e->result = success; +} + +/* Verification callback */ + +void silc_softacc_verify_cb(SilcBool success, void *context) +{ + SilcSoftaccExec e = context; + SilcStack stack = e->stack; + + silc_stack_pop(stack); + e->result = success; +} + +/* Accelerator thread */ + +void silc_softacc_thread(SilcSchedule schedule, void *context) +{ + SilcSoftaccExec e = context; + + if (e->aborted) + return; + + SILC_LOG_DEBUG(("Execute type %d", e->type)); + + /* Call the operation */ + switch (e->type) { + case SILC_SOFTACC_ENCRYPT: + silc_pkcs_encrypt(e->key.public_key, e->src, e->src_len, e->rng, + silc_softacc_data_cb, e); + break; + + case SILC_SOFTACC_DECRYPT: + silc_pkcs_decrypt(e->key.private_key, e->src, e->src_len, + silc_softacc_data_cb, e); + break; + + case SILC_SOFTACC_SIGN: + silc_pkcs_sign(e->key.private_key, e->src, e->src_len, e->compute_hash, + e->hash, silc_softacc_data_cb, e); + break; + + case SILC_SOFTACC_VERIFY: + silc_pkcs_verify(e->key.public_key, e->src, e->src_len, e->data, + e->data_len, e->hash, silc_softacc_verify_cb, e); + break; + } +} + +/* Accelerate public key */ + +SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_softacc_acc_public_key) +{ + SilcSoftaccPublicKey pubkey; + + if (!sa) { + SILC_LOG_ERROR(("Software accelerator not initialized")); + return FALSE; + } + + pubkey = silc_calloc(1, sizeof(*pubkey)); + if (!pubkey) + return FALSE; + pubkey->key = key; + + *ret_public_key = pubkey; + + return TRUE; +} + +/* Accelerate private key */ + +SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_softacc_acc_private_key) +{ + SilcSoftaccPrivateKey privkey; + + if (!sa) { + SILC_LOG_ERROR(("Software accelerator not initialized")); + return FALSE; + } + + privkey = silc_calloc(1, sizeof(*privkey)); + if (!privkey) + return FALSE; + privkey->key = key; + + *ret_private_key = privkey; + + return TRUE; +} + +/* Free public key */ + +SILC_PKCS_ALG_PUBLIC_KEY_FREE(silc_softacc_free_public_key) +{ + silc_free(public_key); +} + +/* Free private key */ + +SILC_PKCS_ALG_PRIVATE_KEY_FREE(silc_softacc_free_private_key) +{ + silc_free(private_key); +} + +/* Accelerated encrypt */ + +SILC_PKCS_ALG_ENCRYPT(silc_softacc_encrypt) +{ + SilcSoftaccPublicKey pubkey = public_key; + SilcStack stack; + SilcSoftaccExec e; + + SILC_LOG_DEBUG(("Encrypt")); + + if (!sa) { + SILC_LOG_ERROR(("Software accelerator not initialized")); + encrypt_cb(FALSE, NULL, 0, context); + return NULL; + } + + stack = silc_stack_alloc(2048, silc_crypto_stack()); + + e = silc_scalloc(stack, 1, sizeof(*e)); + if (!e) { + silc_stack_free(stack); + encrypt_cb(FALSE, NULL, 0, context); + return NULL; + } + + silc_stack_push(stack, NULL); + + e->stack = stack; + e->type = SILC_SOFTACC_ENCRYPT; + e->src = silc_smemdup(stack, src, src_len); + e->src_len = src_len; + e->rng = rng; + e->key.public_key = pubkey->key; + e->cb.encrypt_cb = encrypt_cb; + e->context = context; + silc_async_init(&e->op, silc_softacc_abort, NULL, e); + + /* Run */ + silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e, + silc_softacc_completion, e); + + return &e->op; +} + +/* Acceleted decrypt */ + +SILC_PKCS_ALG_DECRYPT(silc_softacc_decrypt) +{ + SilcSoftaccPrivateKey privkey = private_key; + SilcStack stack; + SilcSoftaccExec e; + + SILC_LOG_DEBUG(("Decrypt")); + + if (!sa) { + SILC_LOG_ERROR(("Software accelerator not initialized")); + decrypt_cb(FALSE, NULL, 0, context); + return NULL; + } + + stack = silc_stack_alloc(2048, silc_crypto_stack()); + + e = silc_scalloc(stack, 1, sizeof(*e)); + if (!e) { + silc_stack_free(stack); + decrypt_cb(FALSE, NULL, 0, context); + return NULL; + } + + silc_stack_push(stack, NULL); + + e->stack = stack; + e->type = SILC_SOFTACC_DECRYPT; + e->src = silc_smemdup(stack, src, src_len); + e->src_len = src_len; + e->key.private_key = privkey->key; + e->cb.decrypt_cb = decrypt_cb; + e->context = context; + silc_async_init(&e->op, silc_softacc_abort, NULL, e); + + /* Run */ + silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e, + silc_softacc_completion, e); + + return &e->op; +} + +/* Accelerated signature */ + +SILC_PKCS_ALG_SIGN(silc_softacc_sign) +{ + SilcSoftaccPrivateKey privkey = private_key; + SilcStack stack; + SilcSoftaccExec e; + + SILC_LOG_DEBUG(("Sign")); + + if (!sa) { + SILC_LOG_ERROR(("Software accelerator not initialized")); + sign_cb(FALSE, NULL, 0, context); + return NULL; + } + + stack = silc_stack_alloc(2048, silc_crypto_stack()); + + e = silc_scalloc(stack, 1, sizeof(*e)); + if (!e) { + silc_stack_free(stack); + sign_cb(FALSE, NULL, 0, context); + return NULL; + } + + silc_stack_push(stack, NULL); + + e->stack = stack; + e->type = SILC_SOFTACC_SIGN; + e->src = silc_smemdup(stack, src, src_len); + e->src_len = src_len; + e->compute_hash = compute_hash; + e->hash = hash; + e->key.private_key = privkey->key; + e->cb.sign_cb = sign_cb; + e->context = context; + silc_async_init(&e->op, silc_softacc_abort, NULL, e); + + /* Run */ + silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e, + silc_softacc_completion, e); + + return &e->op; +} + +/* Accelerated verification */ + +SILC_PKCS_ALG_VERIFY(silc_softacc_verify) +{ + SilcSoftaccPublicKey pubkey = public_key; + SilcStack stack; + SilcSoftaccExec e; + + SILC_LOG_DEBUG(("Verify")); + + if (!sa) { + SILC_LOG_ERROR(("Software accelerator not initialized")); + verify_cb(FALSE, context); + return NULL; + } + + stack = silc_stack_alloc(2048, silc_crypto_stack()); + + e = silc_scalloc(stack, 1, sizeof(*e)); + if (!e) { + silc_stack_free(stack); + verify_cb(FALSE, context); + return NULL; + } + + silc_stack_push(stack, NULL); + + e->stack = stack; + e->type = SILC_SOFTACC_VERIFY; + e->src = silc_smemdup(stack, signature, signature_len); + e->src_len = signature_len; + e->data = silc_smemdup(stack, data, data_len); + e->data_len = data_len; + e->hash = hash; + e->key.public_key = pubkey->key; + e->cb.verify_cb = verify_cb; + e->context = context; + silc_async_init(&e->op, silc_softacc_abort, NULL, e); + + /* Run */ + silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e, + silc_softacc_completion, e); + + return &e->op; +} diff --git a/lib/silcacc/softacc.h b/lib/silcacc/softacc.h new file mode 100644 index 00000000..a359736e --- /dev/null +++ b/lib/silcacc/softacc.h @@ -0,0 +1,37 @@ +/* + + softacc.h + + Author: Pekka Riikonen + + Copyright (C) 2007 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef SOFTACC_H +#define SOFTACC_H + +/* The software accelerator */ +extern DLLAPI const SilcAcceleratorStruct softacc; + +SilcBool silc_softacc_init(SilcSchedule schedule, va_list va); +SilcBool silc_softacc_uninit(void); +SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_softacc_acc_public_key); +SILC_PKCS_ALG_PUBLIC_KEY_FREE(silc_softacc_free_public_key); +SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_softacc_acc_private_key); +SILC_PKCS_ALG_PRIVATE_KEY_FREE(silc_softacc_free_private_key); +SILC_PKCS_ALG_ENCRYPT(silc_softacc_encrypt); +SILC_PKCS_ALG_DECRYPT(silc_softacc_decrypt); +SILC_PKCS_ALG_SIGN(silc_softacc_sign); +SILC_PKCS_ALG_VERIFY(silc_softacc_verify); + +#endif /* SOFTACC_H */ diff --git a/lib/silcacc/tests/Makefile.am b/lib/silcacc/tests/Makefile.am new file mode 100644 index 00000000..b431654f --- /dev/null +++ b/lib/silcacc/tests/Makefile.am @@ -0,0 +1,27 @@ +# +# Makefile.am +# +# Author: Pekka Riikonen +# +# Copyright (C) 2007 Pekka Riikonen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign + +bin_PROGRAMS = test_softacc + +test_softacc_SOURCES = test_softacc.c + +LIBS = $(SILC_COMMON_LIBS) +LDADD = -L.. -L../.. -lsilc -lsilcacc + +include $(top_srcdir)/Makefile.defines.in diff --git a/lib/silcacc/tests/test_softacc.c b/lib/silcacc/tests/test_softacc.c new file mode 100644 index 00000000..066fd2a4 --- /dev/null +++ b/lib/silcacc/tests/test_softacc.c @@ -0,0 +1,88 @@ +/* Software accelerator tests */ + +#include "silc.h" + +SilcSchedule schedule; +SilcPublicKey public_key, accpub; +SilcPrivateKey private_key, accprv; +SilcHash hash; +unsigned char data[] = "Single block msg"; +int data_len = 16; +int s = 100; + +void sign_compl(SilcBool success, const unsigned char *signature, + SilcUInt32 signature_len, void *context) +{ + SILC_LOG_DEBUG(("Sign compl %s", success ? "Ok" : "failed")); +} + +SILC_TASK_CALLBACK(quit) +{ + silc_schedule_stop(schedule); +} + +SILC_TASK_CALLBACK(sign) +{ + silc_pkcs_sign(accprv, data, data_len, TRUE, hash, sign_compl, NULL); + if (--s > 0) + silc_schedule_task_add_timeout(schedule, sign, NULL, 0, 70000); +} + +int main(int argc, char **argv) +{ + SilcBool success = FALSE; + SilcAccelerator softacc; + + if (argc > 1 && !strcmp(argv[1], "-d")) { + silc_log_debug(TRUE); + silc_log_quick(TRUE); + silc_log_debug_hexdump(TRUE); + silc_log_set_debug_string("*acc*"); + } + + silc_crypto_init(NULL); + if (!silc_hash_alloc("sha1", &hash)) + goto err; + + if (!silc_create_key_pair("rsa", 2048, "pubkey.pub", "privkey.prv", NULL, + "", &public_key, &private_key, FALSE)) + goto err; + + schedule = silc_schedule_init(0, NULL, NULL); + + softacc = silc_acc_find("softacc"); + if (!softacc) + goto err; + + if (!silc_acc_init(softacc, schedule, "min_threads", 2, "max_threads", + 8, NULL)) + goto err; + + accpub = silc_acc_public_key(softacc, public_key); + if (!accpub) + goto err; + accprv = silc_acc_private_key(softacc, private_key); + if (!accprv) + goto err; + + if (silc_acc_get_public_key(softacc, accpub) != public_key) + goto err; + if (silc_acc_get_private_key(softacc, accprv) != private_key) + goto err; + + silc_schedule_task_add_timeout(schedule, sign, NULL, 0, 1); + silc_schedule_task_add_timeout(schedule, quit, NULL, 15, 0); + silc_schedule(schedule); + + silc_acc_uninit(softacc); + silc_schedule_uninit(schedule); + silc_crypto_uninit(); + + success = TRUE; + + err: + SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE")); + fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE"); + + return success; +}