--- /dev/null
+/*
+
+ softacc_pkcs.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2007 - 2008 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 "silccrypto.h"
+#include "softacc.h"
+#include "softacc_i.h"
+
+/* The public and private key accelerator. We perform the public key and
+ private key operations in threads. Threads are run in the thread pool. */
+
+/************************** 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 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;
+
+/****************************** PKCS ALG API ********************************/
+
+/* Abort operation */
+
+void silc_softacc_pkcs_abort(SilcAsyncOperation op, void *context)
+{
+ SilcSoftaccExec e = context;
+ e->aborted = TRUE;
+}
+
+/* Accelerator completion, executed in main thread. */
+
+SILC_TASK_CALLBACK(silc_softacc_pkcs_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->result_data);
+ silc_sfree(stack, e);
+ silc_stack_free(stack);
+}
+
+/* Callback for encrypt, decrypt and signature */
+
+void silc_softacc_pkcs_data_cb(SilcBool success, const unsigned char *data,
+ SilcUInt32 data_len, void *context)
+{
+ SilcSoftaccExec e = context;
+ SilcStack stack = e->stack;
+
+ /* Pop e->src */
+ 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_pkcs_verify_cb(SilcBool success, void *context)
+{
+ SilcSoftaccExec e = context;
+ SilcStack stack = e->stack;
+
+ /* Pop e->src and e->data from memory */
+ silc_stack_pop(stack);
+
+ e->result = success;
+}
+
+/* Accelerator thread */
+
+void silc_softacc_pkcs_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_pkcs_data_cb, e);
+ break;
+
+ case SILC_SOFTACC_DECRYPT:
+ silc_pkcs_decrypt(e->key.private_key, e->src, e->src_len,
+ silc_softacc_pkcs_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, e->rng, silc_softacc_pkcs_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_pkcs_verify_cb, e);
+ break;
+ }
+}
+
+/* Accelerate public key */
+
+SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_softacc_acc_public_key)
+{
+ SilcSoftaccPublicKey pubkey;
+ SilcSoftacc sa;
+
+ sa = silc_global_get_var("softacc", FALSE);
+ if (!sa || !sa->schedule) {
+ 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;
+ SilcSoftacc sa;
+
+ sa = silc_global_get_var("softacc", FALSE);
+ if (!sa || !sa->schedule) {
+ 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;
+ SilcSoftacc sa;
+
+ SILC_LOG_DEBUG(("Encrypt"));
+
+ sa = silc_global_get_var("softacc", FALSE);
+ if (!sa || !sa->schedule) {
+ 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_pkcs_abort, NULL, e);
+
+ /* Run */
+ silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_pkcs_thread, e,
+ silc_softacc_pkcs_completion, e);
+
+ return &e->op;
+}
+
+/* Acceleted decrypt */
+
+SILC_PKCS_ALG_DECRYPT(silc_softacc_decrypt)
+{
+ SilcSoftaccPrivateKey privkey = private_key;
+ SilcStack stack;
+ SilcSoftaccExec e;
+ SilcSoftacc sa;
+
+ SILC_LOG_DEBUG(("Decrypt"));
+
+ sa = silc_global_get_var("softacc", FALSE);
+ if (!sa || !sa->schedule) {
+ 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_pkcs_abort, NULL, e);
+
+ /* Run */
+ silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_pkcs_thread, e,
+ silc_softacc_pkcs_completion, e);
+
+ return &e->op;
+}
+
+/* Accelerated signature */
+
+SILC_PKCS_ALG_SIGN(silc_softacc_sign)
+{
+ SilcSoftaccPrivateKey privkey = private_key;
+ SilcStack stack;
+ SilcSoftaccExec e;
+ SilcSoftacc sa;
+
+ SILC_LOG_DEBUG(("Sign"));
+
+ sa = silc_global_get_var("softacc", FALSE);
+ if (!sa || !sa->schedule) {
+ 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->rng = rng;
+ 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_pkcs_abort, NULL, e);
+
+ /* Run */
+ silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_pkcs_thread, e,
+ silc_softacc_pkcs_completion, e);
+
+ return &e->op;
+}
+
+/* Accelerated verification */
+
+SILC_PKCS_ALG_VERIFY(silc_softacc_verify)
+{
+ SilcSoftaccPublicKey pubkey = public_key;
+ SilcStack stack;
+ SilcSoftaccExec e;
+ SilcSoftacc sa;
+
+ SILC_LOG_DEBUG(("Verify"));
+
+ sa = silc_global_get_var("softacc", FALSE);
+ if (!sa || !sa->schedule) {
+ 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_pkcs_abort, NULL, e);
+
+ /* Run */
+ silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_pkcs_thread, e,
+ silc_softacc_pkcs_completion, e);
+
+ return &e->op;
+}