Added cipher acceleration to SILC Accelerator. Added cipher softacc.
authorPekka Riikonen <priikone@silcnet.org>
Thu, 28 Feb 2008 17:50:06 +0000 (19:50 +0200)
committerPekka Riikonen <priikone@silcnet.org>
Thu, 28 Feb 2008 17:50:06 +0000 (19:50 +0200)
The SILC Accelerator API now has silc_acc_cipher to accelerate ciphers.
The accelerators must internally implement the SILC Cipher API.

Added cipher software accelerator.  Cipher accelerator accelerates
ciphers using counter mode by precomputing the CTR key stream in
threads.  Encryption and decryption uses the precomputed key stream
and gets significant speed improvement in the process.  The threads
are reserved from the thread pool and they remain reserved as long
as the cipher is accelerated.

As a queue we use SilcThreadQueue from SRT which handles locking and
waiting automatically and supports multiple pipes for multiple key
streams, so it makes this whole thing very simple.

It can accelerate any cipher but AES is especially optimized.

Split the softacc into several files, own files for cipher and PKCS
acceleration.  Added test programs.

15 files changed:
TODO
distdir/crypto
lib/silcacc/Makefile.ad
lib/silcacc/silcacc.c
lib/silcacc/silcacc.h
lib/silcacc/silcacc_cipher.c [new file with mode: 0644]
lib/silcacc/silcacc_pkcs.c
lib/silcacc/softacc.c
lib/silcacc/softacc.h
lib/silcacc/softacc_cipher.c [new file with mode: 0644]
lib/silcacc/softacc_i.h [new file with mode: 0644]
lib/silcacc/softacc_pkcs.c [new file with mode: 0644]
lib/silcacc/tests/Makefile.am
lib/silcacc/tests/test_softacc_cipher.c [new file with mode: 0644]
lib/silcacc/tests/test_softacc_cipher2.c [new file with mode: 0644]

diff --git a/TODO b/TODO
index 1308c5a302afc2fb308efc6fe7599cd084e5be39..d7be8e6cecd616f69f218a003b5d908d603d27f9 100644 (file)
--- a/TODO
+++ b/TODO
@@ -126,30 +126,6 @@ SKR Library, lib/silcskr/
 SILC Accelerator Library
 ========================
 
- o Add SilcCipher support to SilcAccelerator and software accelerator.
-   Accelerate at least ciphers using CTR mode which can be done in
-   parallel.  Do it in producer/consumer fashion where threads generate
-   key stream and other thread(s) encrypt using the key stream.
-
- o Add init options to SilcAcceleratorObject as a SilcAcceleratorOption
-   structure.  Each accelerator defines the options that they support and
-   can be retrieved from the SilcAccelerator with silc_acc_get_options.
-   The format must also be machine parseable.  The structure can be of the
-   following format:
-
-       typedef struct SilcAcceleratorOptionStruct {
-         const char *option;                   /* Option name */
-         const char *display_name;             /* Option displayable name */
-         SilcParamType type;                   /* Option data format */
-         void *default_value;                  /* Option's default value */
-         SilcUInt32 default_value_len;         /* Default value length */
-       } *SilcAcceleratorOption;
-
-   For software accelerator it could be for example:
-
-   { "min_threads", "Minimum threads", SILC_PARAM_UINT32, (void *)2, 4 },
-   { "max_threads", "Maximum threads", SILC_PARAM_UINT32, (void *)4, 4 },
-
  o Diffie-Hellman acceleration
 
  o SILC Accelerator API.  Provides generic way to use different kind of
@@ -161,6 +137,11 @@ SILC Accelerator Library
    public key and private key operations are executed in threads.
    (***DONE)
 
+ o Add SilcCipher support to SilcAccelerator and software accelerator.
+   Accelerate at least ciphers using CTR mode which can be done in
+   parallel.  Do it in producer/consumer fashion where threads generate
+   key stream and other thread(s) encrypt using the key stream. (***DONE)
+
 
 lib/silcmath
 ============
index 4dd3231d3ab5b4e4938898037fe0f0be822bee04..c3292dfdcf4d6c54922a4ab0527267f9e47d1a38 100644 (file)
@@ -12,11 +12,16 @@ license-header distdir/GPL-header distdir/CRYPTO-header
 # Distdefs
 define SILC_DIST_SSH
 define SILC_DIST_PGP
-define SILC_DIST_TMA
 define SILC_DIST_ASN1UTILS
 
-# TFM not enabled for now
-#define SILC_DIST_TFM
+# Math library
+define SILC_DIST_TFM
+#define SILC_DIST_TMA
+
+# Accelerator library
+define SILC_DIST_SOFTACC
+define SILC_DIST_SOFTACC_PKCS
+define SILC_DIST_SOFTACC_CIPHER
 
 # Includes
 include README.WIN32
index f1a3971cd551db0db903b4c672a24d4f481628c8..099c04b900effd8dd4bde669291a39c446e8a861 100644 (file)
@@ -19,10 +19,27 @@ AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
 noinst_LTLIBRARIES = libsilcacc.la
 
-libsilcacc_la_SOURCES = silcacc.c silcacc_pkcs.c softacc.c
+libsilcacc_la_SOURCES = silcacc.c              \
+                       silcacc_pkcs.c          \
+                       silcacc_cipher.c        \
+                       silcacc.h               \
+#ifdef SILC_DIST_SOFTACC
+                       softacc.c               \
+                       softacc.h               \
+                       softacc_i.h             \
+#ifdef SILC_DIST_SOFTACC_PKCS
+                       softacc_pkcs.c          \
+#endif SILC_DIST_SOFTACC_PKCS
+#ifdef SILC_DIST_SOFTACC_CIPHER
+                       softacc_cipher.c        \
+#endif SILC_DIST_SOFTACC_CIPHER
+#endif SILC_DIST_SOFTACC
 
-include_HEADERS = silcacc.h
+include_HEADERS =      silcacc.h               \
+#ifdef SILC_DIST_SOFTACC
+                       softacc.h
+#endif SILC_DIST_SOFTACC
 
-EXTRA_DIST = *.h $(SILC_EXTRA_DIST)
+EXTRA_DIST = $(SILC_EXTRA_DIST)
 
 include $(top_srcdir)/Makefile.defines.in
index 44fa5bdeae03161d9e02b0e885ab289903ea4b2b..5e3dd5cae5a62368bf8252c24d6900b8439560d4 100644 (file)
 
 /************************** 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[] =
 {
@@ -43,13 +38,18 @@ const SilcAcceleratorStruct *silc_default_accs[] =
 
 SilcBool silc_acc_register(const SilcAccelerator acc)
 {
+  SilcDList silc_acc_list;
+
   if (!acc)
     return FALSE;
 
+  silc_acc_list = silc_global_get_var("silc_acc_list", FALSE);
   if (!silc_acc_list) {
-    silc_acc_list = silc_dlist_init();
+    silc_acc_list = silc_global_set_var("silc_acc_list",
+                                       sizeof(*silc_acc_list), NULL, FALSE);
     if (!silc_acc_list)
       return FALSE;
+    silc_dlist_init_static(silc_acc_list);
   }
 
   SILC_LOG_DEBUG(("Register accelerator %p, name %s", acc, acc->name));
@@ -62,19 +62,20 @@ SilcBool silc_acc_register(const SilcAccelerator acc)
 
 void silc_acc_unregister(SilcAccelerator acc)
 {
+  SilcDList silc_acc_list;
+
   if (!acc)
     return;
 
+  silc_acc_list = silc_global_get_var("silc_acc_list", FALSE);
   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;
-  }
+  if (!silc_dlist_count(silc_acc_list))
+    silc_global_del_var("silc_acc_list", FALSE);
 }
 
 /* Initialize accelerator */
@@ -111,6 +112,7 @@ SilcBool silc_acc_uninit(SilcAccelerator acc)
 
 SilcDList silc_acc_get_supported(void)
 {
+  SilcDList silc_acc_list;
   SilcDList list;
   SilcAccelerator acc;
   int i;
@@ -119,6 +121,7 @@ SilcDList silc_acc_get_supported(void)
   if (!list)
     return NULL;
 
+  silc_acc_list = silc_global_get_var("silc_acc_list", FALSE);
   if (silc_acc_list) {
     silc_dlist_start(silc_acc_list);
     while ((acc = silc_dlist_get(silc_acc_list)))
@@ -135,6 +138,7 @@ SilcDList silc_acc_get_supported(void)
 
 SilcAccelerator silc_acc_find(const char *name)
 {
+  SilcDList silc_acc_list;
   SilcAccelerator acc;
   int i;
 
@@ -143,6 +147,7 @@ SilcAccelerator silc_acc_find(const char *name)
 
   SILC_LOG_DEBUG(("Find accelerator %s", name));
 
+  silc_acc_list = silc_global_get_var("silc_acc_list", FALSE);
   if (silc_acc_list) {
     silc_dlist_start(silc_acc_list);
     while ((acc = silc_dlist_get(silc_acc_list))) {
index a7ec70b2fe26f648f38ac17c4f1e5f334dbccc14..040f1c692a8999ffde8e73679d9c92fc32ea1863 100644 (file)
 
 */
 
-/****h* silcskr/SILC Accelerator Interface
+/****h* silcacc/Crypto Accelerator Interface
  *
  * DESCRIPTION
  *
+ * SILC Crypto Accelerator Interface provides a generic interface for
+ * cryptographic accelerators.  The interface can access different kind of
+ * accelerators, such as hardware accelerators.  Using an accelerator can
+ * significantly improve encryption, decryption, signature and verification
+ * performance.
+ *
+ * Third-party accelerators can be registered into the accelerator interface
+ * and used through the same generic interface.
+ *
+ * The interface can be used to accelerate public and private keys, and
+ * ciphers.
+ *
  ***/
 
 #ifndef SILCACC_H
@@ -47,9 +59,9 @@ typedef struct SilcAcceleratorObject {
                   va_list va);             /* Initialize accelerator */
   SilcBool (*uninit)(void);                /* Uninitialize accelerator */
   const SilcPKCSAlgorithm *pkcs;            /* Accelerated PKCS algorithms */
+  const SilcCipherObject *cipher;          /* Accelerated ciphers */
 #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 */
@@ -188,6 +200,10 @@ const char *silc_acc_get_name(SilcAccelerator acc);
  *    The associated `public_key' can be retrieved from the returned
  *    public key by calling silc_acc_get_public_key.
  *
+ *    If this returns NULL the public key could not be accelerated.  This
+ *    usually should not be considered serious error.  Instead, the public
+ *    key should be used without acceleration.
+ *
  ***/
 SilcPublicKey silc_acc_public_key(SilcAccelerator acc,
                                  SilcPublicKey public_key);
@@ -211,6 +227,10 @@ SilcPublicKey silc_acc_public_key(SilcAccelerator acc,
  *    The associated `private_key' can be retrieved from the returned
  *    private key by calling silc_acc_get_private_key.
  *
+ *    If this returns NULL the private key could not be accelerated.  This
+ *    usually should not be considered serious error.  Instead, the private
+ *    key should be used without acceleration.
+ *
  ***/
 SilcPrivateKey silc_acc_private_key(SilcAccelerator acc,
                                    SilcPrivateKey private_key);
@@ -249,4 +269,45 @@ SilcPublicKey silc_acc_get_public_key(SilcAccelerator acc,
 SilcPrivateKey silc_acc_get_private_key(SilcAccelerator acc,
                                        SilcPrivateKey private_key);
 
+/****f* silcacc/silc_acc_cipher
+ *
+ * SYNOPSIS
+ *
+ *    SilcCipher silc_acc_cipher(SilcAccelerator acc, SilcCipher cipher);
+ *
+ * DESCRIPTION
+ *
+ *    Accelerate the cipher indicated by `cipher'.  Returns new accelerated
+ *    SilcCipher context.  It can be used just as normal cipher and must be
+ *    freed by calilng silc_cipher_free.  The associated `cipher' is not
+ *    freed when the accelerated cipher is freed.  The `cipher' must not be
+ *    freed as long as it is accelerated.
+ *
+ *    When key and IV is set for the accelerated cipher, it is also set to
+ *    the associated cipher.
+ *
+ *    The associated `cipher' can be retrieved from the accelerated cipher
+ *    by calling silc_acc_get_cipher.
+ *
+ *    If this returns NULL the cipher could not be accelerated.  This
+ *    usually should not be considered serious error.  Instead, the cipher
+ *    should be used without acceleration.
+ *
+ ***/
+SilcCipher silc_acc_cipher(SilcAccelerator acc, SilcCipher cipher);
+
+/****f* silcacc/silc_acc_get_cipher
+ *
+ * SYNOPSIS
+ *
+ *    SilcCipher silc_acc_get_cipher(SilcAccelerator acc, SilcCipher cipher);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the underlaying cipher from the accelerated cipher indicated
+ *    by `cipher'.  Returns NULL if `cipher' is not accelerated cipher.
+ *
+ ***/
+SilcCipher silc_acc_get_cipher(SilcAccelerator acc, SilcCipher cipher);
+
 #endif /* SILCACC_H */
diff --git a/lib/silcacc/silcacc_cipher.c b/lib/silcacc/silcacc_cipher.c
new file mode 100644 (file)
index 0000000..e87091b
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+
+  silcacc_cipher.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 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"
+
+/************************** Types and definitions ***************************/
+
+SILC_CIPHER_API_SET_KEY(acc_cipher);
+SILC_CIPHER_API_SET_IV(acc_cipher);
+SILC_CIPHER_API_ENCRYPT(acc_cipher);
+SILC_CIPHER_API_DECRYPT(acc_cipher);
+SILC_CIPHER_API_INIT(acc_cipher);
+SILC_CIPHER_API_UNINIT(acc_cipher);
+
+/* Accelerated cipher */
+typedef struct SilcAcceleratorCipherStruct {
+  SilcCipher cipher;           /* Associated cipher */
+  SilcCipher acc_cipher;       /* Accelerator cipher */
+} *SilcAcceleratorCipher;
+
+/************************** Accelerator Cipher API **************************/
+
+/* The Cipher API for the accelerated cipher is simply a wrapper.  It
+   calls the SILC Cipher API for the accelerator cipher. */
+
+const SilcCipherObject silc_acc_ciph =
+{
+  "silc_acc_cipher",
+  "silc_acc_cipher",
+
+  silc_acc_cipher_set_key,
+  silc_acc_cipher_set_iv,
+  silc_acc_cipher_encrypt,
+  silc_acc_cipher_decrypt,
+  silc_acc_cipher_init,
+  silc_acc_cipher_uninit,
+
+  0, 0, 0, 0
+};
+
+SILC_CIPHER_API_SET_KEY(acc_cipher)
+{
+  SilcAcceleratorCipher c = context;
+
+  /* Set key for the associated cipher too */
+  silc_cipher_set_key(c->cipher, key, keylen, encryption);
+
+  /* Set key for accelerator */
+  return silc_cipher_set_key(c->acc_cipher, key, keylen, encryption);
+}
+
+SILC_CIPHER_API_SET_IV(acc_cipher)
+{
+  SilcAcceleratorCipher c = context;
+
+  /* Set IV for the associated cipher too */
+  silc_cipher_set_iv(c->cipher, iv);
+
+  /* Set IV for accelerator */
+  silc_cipher_set_iv(c->acc_cipher, iv);
+}
+
+SILC_CIPHER_API_ENCRYPT(acc_cipher)
+{
+  SilcAcceleratorCipher c = context;
+  return silc_cipher_encrypt(c->acc_cipher, src, dst, len, iv);
+}
+
+SILC_CIPHER_API_DECRYPT(acc_cipher)
+{
+  SilcAcceleratorCipher c = context;
+  return silc_cipher_decrypt(c->acc_cipher, src, dst, len, iv);
+}
+
+SILC_CIPHER_API_INIT(acc_cipher)
+{
+  /* This operation is never called */
+  return NULL;
+}
+
+SILC_CIPHER_API_UNINIT(acc_cipher)
+{
+  SilcAcceleratorCipher c = context;
+  SilcCipherObject *acc_ops = c->acc_cipher->cipher;
+
+  /* Free the accelerator cipher and its operations we allocated earlier. */
+  silc_cipher_free(c->acc_cipher);
+  silc_free(acc_ops);
+
+  /* Free our operations too */
+  silc_free(ops);
+}
+
+/*************************** SILC Accelerator API ***************************/
+
+/* Accelerate cipher */
+
+SilcCipher silc_acc_cipher(SilcAccelerator acc, SilcCipher cipher)
+{
+  SilcCipher c;
+  SilcAcceleratorCipher acc_cipher;
+  const SilcCipherObject *alg;
+  int i;
+
+  if (!acc || !cipher)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Accelerate cipher %p with accelerator %s",
+                 cipher, acc->name));
+
+  if (!acc->cipher) {
+    SILC_LOG_ERROR(("Accelerator '%s' does not support cipher acceleration ",
+                   acc->name));
+    return NULL;
+  }
+
+  if (silc_acc_get_cipher(NULL, cipher)) {
+    SILC_LOG_DEBUG(("Cipher %p is already accelerated", cipher));
+    return NULL;
+  }
+
+  /* Check that accelerator supports this cipher algorithm */
+  alg = cipher->cipher;
+  for (i = 0; acc->cipher[i].alg_name; i++) {
+    if ((!strcmp(acc->cipher[i].alg_name, alg->alg_name) ||
+        !strcmp(acc->cipher[i].alg_name, "any")) &&
+       (acc->cipher[i].mode == alg->mode ||
+        acc->cipher[i].mode == 0) &&
+       (acc->cipher[i].key_len == alg->key_len ||
+        acc->cipher[i].key_len == 0)) {
+      alg = NULL;
+      break;
+    }
+  }
+  if (alg) {
+    SILC_LOG_DEBUG(("Accelerator %s does not support %s (mode %d) "
+                   "acceleration", acc->name, alg->name, alg->mode));
+    return NULL;
+  }
+
+  /* Allocate cipher context for the SILC Cipher API */
+  c = silc_calloc(1, sizeof(*c));
+  if (!c)
+    return NULL;
+
+  /* Allocate cipher operations */
+  c->cipher = silc_calloc(1, sizeof(SilcCipherObject));
+  if (!c->cipher) {
+    silc_free(c);
+    return NULL;
+  }
+  *c->cipher = silc_acc_ciph;
+
+  /* Allocate cipher context */
+  c->context = acc_cipher = silc_calloc(1, sizeof(*acc_cipher));
+  if (!acc_cipher) {
+    silc_free(c->cipher);
+    silc_free(c);
+    return NULL;
+  }
+  acc_cipher->cipher = cipher;
+
+  /* Allocate the actual algorithm accelerator. */
+  acc_cipher->acc_cipher = silc_calloc(1, sizeof(*acc_cipher->acc_cipher));
+  if (!acc_cipher->acc_cipher) {
+    silc_free(c->cipher);
+    silc_free(c);
+    silc_free(acc_cipher);
+  }
+
+  /* Initialize the algorithm accelerator */
+  acc_cipher->acc_cipher->context =
+    acc->cipher[i].init((struct SilcCipherObjectStruct *)&acc->cipher[i]);
+  if (!acc_cipher->acc_cipher->context) {
+    silc_free(c->cipher);
+    silc_free(c);
+    silc_free(acc_cipher->acc_cipher);
+    silc_free(acc_cipher);
+    return NULL;
+  }
+
+  /* Allocate algorithm accelerator operations */
+  acc_cipher->acc_cipher->cipher = silc_calloc(1, sizeof(SilcCipherObject));
+  if (!acc_cipher->acc_cipher->cipher) {
+    silc_free(c->cipher);
+    silc_free(c);
+    silc_free(acc_cipher->acc_cipher->context);
+    silc_free(acc_cipher->acc_cipher);
+    silc_free(acc_cipher);
+    return NULL;
+  }
+
+  /* Set algorithm accelerator operations.  They are copied from the
+     accelerator, but algorithm specific things come from associated
+     cipher.  This way accelerators get the associated cipher details. */
+  *acc_cipher->acc_cipher->cipher = acc->cipher[i];
+  acc_cipher->acc_cipher->cipher->alg_name =
+    (char *)silc_cipher_get_alg_name(cipher);
+  acc_cipher->acc_cipher->cipher->key_len = silc_cipher_get_key_len(cipher);
+  acc_cipher->acc_cipher->cipher->block_len =
+    silc_cipher_get_block_len(cipher);
+  acc_cipher->acc_cipher->cipher->iv_len = silc_cipher_get_iv_len(cipher);
+
+  /* Set for the accelerator cipher too */
+  c->cipher->key_len = silc_cipher_get_key_len(cipher);
+  c->cipher->block_len = silc_cipher_get_block_len(cipher);
+  c->cipher->iv_len = silc_cipher_get_iv_len(cipher);
+
+  /* Start the accelerator.  The accelerator is started by setting key
+     with NULL key. */
+  if (!silc_cipher_set_key(acc_cipher->acc_cipher, NULL, 0, FALSE)) {
+    SilcCipherObject *ops = acc_cipher->acc_cipher->cipher;
+    silc_cipher_free(acc_cipher->acc_cipher);
+    silc_free(ops);
+    silc_free(c->cipher);
+    silc_free(c);
+    return NULL;
+  }
+
+  SILC_LOG_DEBUG(("New accelerated cipher %p", c));
+
+  return c;
+}
+
+/* Return underlaying cipher from accelerated cipher. */
+
+SilcCipher silc_acc_get_cipher(SilcAccelerator acc, SilcCipher cipher)
+{
+  SilcAcceleratorCipher acc_cipher;
+
+  if (!cipher || cipher->cipher != &silc_acc_ciph)
+    return NULL;
+
+  acc_cipher = cipher->context;
+
+  return acc_cipher->cipher;
+}
index 72452920b9dd66344a1a480826bae819405624ac..cc666880811b635db25652f8c69b5a2da000a639 100644 (file)
@@ -332,7 +332,7 @@ SilcPublicKey silc_acc_public_key(SilcAccelerator acc,
   /* Accelerate the public key.  Returns accelerator context. */
   if (!acc->pkcs[i].import_public_key(&acc->pkcs[i], public_key, 0,
                                      &acc_pubkey->context)) {
-    SILC_LOG_ERROR(("Error accelerating public key with accelerator '%s'",
+    SILC_LOG_DEBUG(("Error accelerating public key with accelerator '%s'",
                    acc->name));
     silc_free(acc_pubkey);
     silc_free(pubkey->pkcs);
@@ -417,7 +417,8 @@ SilcPrivateKey silc_acc_private_key(SilcAccelerator acc,
   acc_privkey->acc = acc;
   acc_privkey->pkcs_index = i;
 
-  /* Accelerate the public key.  Returns accelerator context. */
+  /* Accelerate the public key.  Returns accelerator context.  The
+     import_public_key operation is used to accelerate the key. */
   if (!acc->pkcs[i].import_private_key(&acc->pkcs[i], private_key, 0,
                                       &acc_privkey->context)) {
     SILC_LOG_ERROR(("Error accelerating private key with accelerator '%s'",
index 44754f0e3bfc3f54df324a3add7aff25053db10e..11e37fdee7e5d410766639e6306b78b0e334da2b 100644 (file)
 
 #include "silccrypto.h"
 #include "softacc.h"
+#include "softacc_i.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
+/* Software accelerator is a thread-pool system where computationally
+   expensive operations are executed in multiple threads for the purpose of
+   off-loading and balancing the computations across multiple processors. */
 
 /************************** 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
+  "softacc", silc_softacc_init, silc_softacc_uninit,
+#ifdef SILC_DIST_SOFTACC_PKCS
+  softacc_pkcs,
+#else /* !SILC_DIST_SOFTACC_PKCS */
+  NULL,
+#endif /* SILC_DIST_SOFTACC_PKCS */
+#ifdef SILC_DIST_SOFTACC_CIPHER
+  softacc_cipher,
+#else /* !SILC_DIST_SOFTACC_CIPHER */
+  NULL,
+#endif /* SILC_DIST_SOFTACC_CIPHER */
 };
 
-/* 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);
-
-*/
+/* Initialize software accelerator */
 
 SilcBool silc_softacc_init(SilcSchedule schedule, va_list va)
 {
-  SilcUInt32 min_threads = SILC_SOFTACC_MIN_THREADS;
-  SilcUInt32 max_threads = SILC_SOFTACC_MAX_THREADS;
+  SilcSoftacc sa;
   char *opt;
 
-  if (!schedule)
-    return FALSE;
-
   /* If already initialized, uninitialize first. */
+  sa = silc_global_get_var("softacc", FALSE);
   if (sa)
     silc_softacc_uninit();
 
+  sa = silc_global_set_var("softacc", sizeof(*sa), NULL, FALSE);
+  if (!sa)
+    return FALSE;
+
+  sa->schedule = schedule;
+  sa->min_threads = SILC_SOFTACC_MIN_THREADS;
+  sa->max_threads = SILC_SOFTACC_MAX_THREADS;
+  sa->cipher_threads = SILC_SOFTACC_CIPHER_THREADS;
+  sa->cipher_blocks = SILC_SOFTACC_CIPHER_BLOCKS;
+  sa->cipher_streams = SILC_SOFTACC_CIPHER_STREAMS;
+
   /* Get options */
   while ((opt = va_arg(va, char *))) {
     if (!strcmp(opt, "min_threads"))
-      min_threads = va_arg(va, SilcUInt32);
+      sa->min_threads = va_arg(va, SilcUInt32);
     else if (!strcmp(opt, "max_threads"))
-      max_threads = va_arg(va, SilcUInt32);
+      sa->max_threads = va_arg(va, SilcUInt32);
+    else if (!strcmp(opt, "cipher_threads"))
+      sa->cipher_threads = va_arg(va, SilcUInt32);
+    else if (!strcmp(opt, "cipher_blocks"))
+      sa->cipher_blocks = va_arg(va, SilcUInt32);
+    else if (!strcmp(opt, "cipher_streams"))
+      sa->cipher_streams = 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)
+  if (!sa->cipher_streams || !sa->cipher_blocks || !sa->cipher_threads)
     return FALSE;
 
-  sa->schedule = schedule;
+  SILC_LOG_DEBUG(("Initialize software accelerator, min_threads %d, "
+                 "max_threads %d", sa->min_threads, sa->max_threads));
 
   /* Start the thread pool */
-  sa->tp = silc_thread_pool_alloc(NULL, min_threads, max_threads, TRUE);
+  sa->tp = silc_thread_pool_alloc(NULL, sa->min_threads,
+                                 sa->max_threads, TRUE);
   if (!sa->tp) {
-    silc_free(sa);
-    sa = NULL;
+    silc_global_del_var("softacc", FALSE);
     return FALSE;
   }
 
@@ -176,365 +103,16 @@ SilcBool silc_softacc_init(SilcSchedule schedule, va_list va)
 
 SilcBool silc_softacc_uninit(void)
 {
+  SilcSoftacc sa;
+
+  sa = silc_global_get_var("softacc", FALSE);
   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->result_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 */
-  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;
-
-  /* Pop e->src and e->data from memory */
-  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, e->rng, 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;
+  silc_global_del_var("softacc", FALSE);
 
   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->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_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;
-}
index a359736e3223cb670fc6397891d9c27887c79d7d..1439321bc797d0df4bb4883635eb00d0b4a15775 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2007 Pekka Riikonen
+  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
 
 */
 
+/****h* silcacc/Software Accelerator
+ *
+ * DESCRIPTION
+ *
+ * Software accelerator is a thread-pool system where computationally
+ * expensive operations are executed in multiple threads for the purpose of
+ * off-loading and balancing the computations across multiple cores and
+ * processors.
+ *
+ * Software accelerator need not be registered with silc_acc_register
+ * because it is registered automatically in SILC Crypto Toolkit, however
+ * it must be initialized with silc_acc_init.
+ *
+ * ACCELERATORS
+ *
+ * Public key and private key accelerator:
+ *
+ * The software accelerator can accelerate public keys and private keys.
+ * The public key and private key operations are executed in threads to
+ * enhance the overall performance of the program and machine when multiple
+ * public key and private key operations need to be executed at the same
+ * time.  This can significantly improve performance, especially in server
+ * applications.
+ *
+ * The accelerated public key and private key can be used with normal
+ * SILC PKCS API.  Internally however the software accelerator is used.
+ * The SilcSchedule must be given as argument to silc_acc_init if the
+ * softacc is used to accelerate public keys and private keys.
+ *
+ * Ciphers:
+ *
+ * The software accelerator can accelerate ciphers.  The only supported
+ * encryption mode is Counter Mode (CTR).  Ciphers with other encryption
+ * modes cannot be accelerated.  The CTR mode is accelerated by pre-computing
+ * the CTR key stream in threads.  This can significantly enhance both
+ * encryption and decryption performance.
+ *
+ * The accelerated cipher can be used with normal SILC Cipher API.
+ * Internally however the software accelerator is used.  Currently only
+ * one limitation exist with accelerated ciphers and SILC Cipher API;  The
+ * optional IV argument in silc_cipher_encrypt and silc_cipher_decrypt
+ * cannot be used.  The IV must be set with silc_cipher_set_iv prior to
+ * encrypting and decrypting.  Usually, this is not an issue for programmer.
+ *
+ * PERFORMANCE
+ *
+ * Ciphers:
+ *
+ * The AES is especially optimized in softacc.  Other ciphers can be used
+ * as well, but their performance does not match that of the AES.
+ *
+ * On dual-core machine the default settings should give very good peak
+ * performance.  In 2008, AES-128 was measured 2.0 Gbit/sec with default
+ * settings.
+ *
+ * On 4-core and 8-core machines the default settings will not give the
+ * best performance.  To get the best performance out, one must commit
+ * system resources (RAM) for softacc.  In 2008, AES-128 was measured 5.68
+ * Gbit/sec with cipher_blocks=65536 and cipher_streams=32 on 4-core
+ * machine (Xeon 5160 3GHz) and 9.08 Gbit/sec with cipher_blocks=65536 and
+ * cipher_streams=64 on 8-core machine (Xeon E5345 2.33GHz).  With default
+ * settings you can expect 1-3 Gbit/sec reduction in peak performance.
+ *
+ * OPTIONS
+ *
+ * The following options can affect the behavior of the softacc and can be
+ * given as argument to the silc_acc_init when initializing the softacc.
+ *
+ * "min_threads"
+ *
+ *  The minimum amount of threads that the softacc will always run.  If
+ *  this isn't given the default number is 0 (does not start any threads
+ *  when initialized).
+ *
+ * "max_threads"
+ *
+ *  The maximum amount of threads the software accelerator can use.  If
+ *  you are using the softacc only for accelerating public key and private
+ *  key operations this number should be the number of CPU cores in your
+ *  machine.  If you are using it also for accelerating ciphers this number
+ *  may need to be fairly large.  Each acclerated cipher will reserve
+ *  "cipher_threads" many threads from the softacc.  Always leave some
+ *  threads free for the public key and private key acceleration to work.
+ *  If this option is not given the default number is 4.
+ *
+ * "cipher_threads"
+ *
+ *  The number of threads each accelerated cipher will use.  Note that,
+ *  each accelerated cipher will reserve this many threads from the softacc.
+ *  The "max_threads" will determine the final maximum number of threads
+ *  the softacc can use.  If the "max_threads" limit is reached no more
+ *  ciphers can be accelerated (also note that if this happens, public key
+ *  and private key acceleration does not work anymore).  The threads are
+ *  reserved as long as the cipher is accelerated.  If this option is not
+ *  given the default number is 2.
+ *
+ * "cipher_blocks"
+ *
+ *  The number of cipher blocks the softacc will pre-compute.  Each cipher
+ *  block consumes 16 or 8 bytes of memory, depending on the size of the
+ *  actual cipher block size.  This value can be used to tweak the
+ *  performance of the softacc.  If this option is not given the default
+ *  number is 4096.  The number must be multiple of 16.
+ *
+ * "cipher_streams"
+ *
+ *  The number of pre-computation streams each accelerated cipher will use.
+ *  Each stream will use "cipher_blocks" many blocks in the stream.  This
+ *  number can be used to tweak the performance of the softacc.  If this
+ *  option is not given the default number is 2 * "cipher_threads".
+ *
+ * EXAMPLE
+ *
+ * // Initialize the software accelerator.
+ * silc_acc_init(SILC_SOFTACC, "min_threads", 2, "max_threads", 8, NULL);
+ *
+ ***/
+
 #ifndef SOFTACC_H
 #define SOFTACC_H
 
-/* The software accelerator */
+/****s* silcacc/softacc
+ *
+ * NAME
+ *
+ *    SILC_SOFTACC
+ *
+ * DESCRIPTION
+ *
+ *    The software accelerator context.  It can be used when initializing
+ *    the accelerator.  It may be used directly with the SILC Accelerator
+ *    Interface after initialization also.
+ *
+ *    Softare accelerator need not be registered with silc_acc_register
+ *    because it is registered automatically in SILC Crypto Toolkit, however
+ *    it must be initialized.
+ *
+ *    The software accelerator must be initialized once per application.  If
+ *    it is initialized again it will be uninitialized first automatically
+ *    and then re-initialized.  When it is not needed anymore (usually when
+ *    the program is ended) it must be uninitialized by calling the
+ *    silc_acc_uninit.
+ *
+ * EXAMPLE
+ *
+ *    // Initialize the software accelerator.
+ *    silc_acc_init(SILC_SOFTACC, "min_threads", 2, "max_threads", 8, NULL);
+ *
+ ***/
 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);
+/****d* silcacc/SILC_SOFTACC
+ *
+ * NAME
+ *
+ *    #define SILC_SOFTACC (SilcAccelerator)&softacc
+ *
+ * DESCRIPTION
+ *
+ *    The name of the software accelerator.
+ *
+ ***/
+#define SILC_SOFTACC (SilcAccelerator)&softacc
+
+/****d* silcacc/SILC_SOFTACC_NAME
+ *
+ * NAME
+ *
+ *    #define SILC_SOFTACC_NAME "softacc"
+ *
+ * DESCRIPTION
+ *
+ *    The name of the software accelerator.
+ *
+ ***/
+#define SILC_SOFTACC_NAME "softacc"
 
 #endif /* SOFTACC_H */
diff --git a/lib/silcacc/softacc_cipher.c b/lib/silcacc/softacc_cipher.c
new file mode 100644 (file)
index 0000000..a8473f0
--- /dev/null
@@ -0,0 +1,817 @@
+/*
+
+  softacc_cipher.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 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.
+
+*/
+
+/* #define SILC_SOFTACC_DEBUG_ON 1 */
+
+#include "silccrypto.h"
+#include "softacc.h"
+#include "softacc_i.h"
+#include "aes_internal.h"
+
+/* Version 1.0 */
+
+/* Cipher accelerator accelerates ciphers using counter mode by precomputing
+   the CTR key stream in threads.  Encryption and decryption uses the
+   precomputed key stream and gets significant speed improvement in the
+   process.  The threads are reserved from the thread pool and they remain
+   reserved as long as the cipher is accelerated.
+
+   As a queue we use SilcThreadQueue from SRT which handles locking and
+   waiting automatically and supports multiple pipes for multiple key
+   streams, so it makes this whole thing very simple.
+
+   This can accelerate any cipher but AES is especially optimized.
+
+   Problems:
+
+   To get the absolutely maximum performance out one must assign lots of
+   RAM to softacc.
+
+*/
+
+/*
+  Benchmarks (version 1.0):
+
+  4-core: 2 x dual-core Xeon 5160 3GHz (Woodcrest), 4 GB RAM
+  -----------------------------------------------------------------------
+  cipher_threads = 4, cipher_blocks = 65536, cipher_streams = 32:
+  aes-128-ctr:     728042.34 KB   710.98 MB   5687.83 Mbit / sec
+  aes-192-ctr:     634662.85 KB   619.79 MB   4958.30 Mbit / sec
+  aes-256-ctr:     555215.22 KB   542.20 MB   4337.62 Mbit / sec
+
+  default settings, cipher_threads = 4:
+  aes-128-ctr:     625568.94 KB   610.91 MB   4887.26 Mbit / sec
+  aes-192-ctr:     572719.08 KB   559.30 MB   4474.37 Mbit / sec
+  aes-256-ctr:     506930.88 KB   495.05 MB   3960.40 Mbit / sec
+
+  8-core: 2 x quad-core Xeon E5345 2.33GHz (Clovertown), 4 GB RAM
+  -----------------------------------------------------------------------
+  cipher_threads = 8, cipher_blocks = 65536, cipher_streams = 64:
+  aes-128-ctr:    1162373.93 KB  1135.13 MB   9081.05 Mbit / sec
+  aes-192-ctr:     994808.64 KB   971.49 MB   7771.94 Mbit / sec
+  aes-256-ctr:     874370.93 KB   853.88 MB   6831.02 Mbit / sec
+
+  default settings, cipher_threads = 8:
+  aes-128-ctr:     805157.74 KB   786.29 MB   6290.29 Mbit / sec
+  aes-192-ctr:     733164.28 KB   715.98 MB   5727.85 Mbit / sec
+  aes-256-ctr:     664677.98 KB   649.10 MB   5192.80 Mbit / sec
+
+  Test setup:
+  - Linux 2.6.20 x86-64
+  - GCC 4.1.2
+  - Yasm 0.5.0.1591
+  - nice -n -20 lib/silcacc/tests/test_softacc_cipher
+
+*/
+
+/************************** Types and definitions ***************************/
+
+/* Software accelerator cipher operations */
+const SilcCipherObject softacc_cipher[] =
+{
+  /* AES */
+  {
+    "aes", "aes",
+    silc_softacc_cipher_aes_set_key,
+    silc_softacc_cipher_aes_set_iv,
+    silc_softacc_cipher_aes_encrypt,
+    silc_softacc_cipher_aes_encrypt,
+    silc_softacc_cipher_init,
+    silc_softacc_cipher_uninit,
+    0, 0, 0,
+    SILC_CIPHER_MODE_CTR,      /* Only CTR mode can be accelerated */
+  },
+
+  /* All other ciphers */
+  {
+    "any", "any",
+    silc_softacc_cipher_set_key,
+    silc_softacc_cipher_set_iv,
+    silc_softacc_cipher_encrypt,
+    silc_softacc_cipher_encrypt,
+    silc_softacc_cipher_init,
+    silc_softacc_cipher_uninit,
+    0, 0, 0,
+    SILC_CIPHER_MODE_CTR,      /* Only CTR mode can be accelerated */
+  },
+
+  {
+    NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, 0, 0, 0, 0,
+  }
+};
+
+/* Block size */
+#define SILC_KEYSTREAM_BLOCK SILC_CIPHER_MAX_IV_SIZE
+
+/* Thread stop signal */
+#define SILC_KEYSTREAM_STOP (void *)0x01
+
+/* Key stream context */
+typedef struct {
+  SilcUInt32 key_index;                              /* Key index in queue */
+  unsigned char ctr[SILC_CIPHER_MAX_IV_SIZE]; /* Counter */
+  unsigned char key[0];                              /* Key stream begins here */
+} *SilcSoftaccCipherKeyStream;
+
+/* Accelerator cipher context */
+typedef struct SilcSoftaccCipherStruct {
+  union {
+    AesContext aes;                          /* AES */
+    SilcCipher ecb;                          /* Other ciphers in ECB mode */
+  } c;
+
+  SilcThreadQueue queue;                     /* Key stream queue */
+  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];  /* Current counter */
+  SilcSoftaccCipherKeyStream *key_stream;     /* Key streams */
+  SilcSoftaccCipherKeyStream cur;            /* Current key stream */
+  SilcUInt32 cur_block;                              /* Current block in key stream */
+  SilcUInt32 cur_index;                              /* Current key stream index */
+  SilcUInt32 pad;                            /* Partial block offset */
+  SilcUInt32 num_key_stream;                 /* Number of key streams */
+  SilcUInt32 cipher_blocks;                  /* Number of cipher blocks */
+  unsigned int cipher_threads : 31;          /* Number of cipher threads */
+  unsigned int key_set : 1;                  /* Set when key is set */
+} *SilcSoftaccCipher;
+
+/************************** Static utility functions ************************/
+
+/* Add value to MSB ordered counter. */
+
+static inline
+void silc_softacc_add_ctr(unsigned char *ctr, SilcUInt32 block_len,
+                         SilcUInt32 val)
+{
+  SilcUInt16 q = 0;
+  int i;
+
+  if (!val)
+    return;
+
+  for (i = block_len - 1; i >= 0; i--) {
+    q += ctr[i] + (val & 0xff);
+    ctr[i] = (q & 0xff);
+    val >>= 8;
+    q >>= 8;
+    if (!val && !q)
+      return;
+  }
+}
+
+/*********************************** AES ************************************/
+
+#define SILC_AES_BLOCK 16
+
+/* Thread destructor */
+
+static SILC_TASK_CALLBACK(silc_softacc_cipher_aes_completion)
+{
+  SilcSoftaccCipher c = context;
+  int i;
+
+  /* Disconnect from key stream queue */
+  if (silc_thread_queue_disconnect(c->queue))
+    return;
+
+  for (i = 0; i < c->num_key_stream; i++)
+    silc_free(c->key_stream[i]);
+  silc_free(c->key_stream);
+  memset(c, 0, sizeof(*c));
+  silc_free(c);
+}
+
+/* Key stream computation thread */
+
+void silc_softacc_cipher_aes_thread(SilcSchedule schedule, void *context)
+{
+  SilcSoftaccCipher c = context;
+  SilcThreadQueue queue = c->queue;
+  SilcSoftaccCipherKeyStream key;
+  SilcUInt32 i, num_key_stream = c->num_key_stream;
+  SilcUInt32 cipher_blocks = c->cipher_blocks;
+  SilcInt32 k;
+  unsigned char *enc_ctr;
+
+  SILC_SOFTACC_DEBUG(("Start CTR precomputation thread"));
+
+  /* Connect to the key stream queue */
+  silc_thread_queue_connect(queue);
+
+  /* Process key streams.  We wait for empty key streams to come from the
+     last pipe in the queue.  Here we precompute the key stream and put them
+     back to the queue. */
+  while (1) {
+    key = silc_thread_queue_pop(queue, num_key_stream, TRUE);
+    if (key == SILC_KEYSTREAM_STOP)
+      break;
+
+    SILC_SOFTACC_DEBUG(("Precompute key stream %p, index %d", key,
+                       key->key_index));
+
+    /* Encrypt */
+    enc_ctr = key->key;
+    for (i = 0; i < cipher_blocks; i++) {
+      for (k = SILC_AES_BLOCK - 1; k >= 0; k--)
+       if (++key->ctr[k])
+         break;
+      aes_encrypt(key->ctr, enc_ctr, &c->c.aes.u.enc);
+      enc_ctr += SILC_AES_BLOCK;
+    }
+
+    SILC_SOFTACC_DEBUG(("Precomputed key stream %p, index %d", key,
+                       key->key_index));
+
+    /* Update counter */
+    silc_softacc_add_ctr(key->ctr, SILC_AES_BLOCK,
+                        (num_key_stream - 1) * cipher_blocks);
+
+    /* Put it back to queue */
+    silc_thread_queue_push(queue, key->key_index, key, FALSE);
+  }
+
+  SILC_SOFTACC_DEBUG(("End CTR precomputation thread"));
+}
+
+/* Set IV.  Also, reset current block, discarding any remaining unused bits in
+   the current key block. */
+
+SILC_CIPHER_API_SET_IV(softacc_cipher_aes)
+{
+  SilcSoftaccCipher c = context;
+  SilcSoftaccCipherKeyStream key;
+  SilcUInt32 i;
+
+  /* If IV is NULL we start new block */
+  if (!iv) {
+    SILC_SOFTACC_DEBUG(("Start new block"));
+
+    if (c->pad < SILC_AES_BLOCK) {
+      c->pad = SILC_AES_BLOCK;
+
+      /* Start new block */
+      if (++c->cur_block == c->cipher_blocks) {
+        SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
+                           c->cur, c->cur->key_index));
+        silc_thread_queue_push(c->queue, c->num_key_stream, c->cur, FALSE);
+        c->cur_index = (c->cur_index + 1) % c->cipher_blocks;
+        c->cur_block = 0;
+        c->cur = NULL;
+      }
+    }
+  } else {
+    /* Start new IV */
+    SILC_SOFTACC_DEBUG(("Start new counter"));
+
+    memcpy(c->iv, iv, SILC_AES_BLOCK);
+
+    if (!c->key_set)
+      return;
+
+    /* Push current key stream back to queue.  We need all of them there
+       below. */
+    if (c->cur)
+      silc_thread_queue_push(c->queue, c->cur->key_index, c->cur, FALSE);
+
+    /* We must get all key streams and update them */
+    for (i = 0; i < c->num_key_stream; i++) {
+      key = silc_thread_queue_pop(c->queue, i, TRUE);
+      memcpy(key->ctr, c->iv, SILC_AES_BLOCK);
+      silc_softacc_add_ctr(key->ctr, SILC_AES_BLOCK, i * c->cipher_blocks);
+      silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
+    }
+
+    c->cur = NULL;
+    c->cur_index = 0;
+    c->cur_block = 0;
+    c->pad = SILC_AES_BLOCK;
+  }
+}
+
+/* Accelerate cipher */
+
+SILC_CIPHER_API_SET_KEY(softacc_cipher_aes)
+{
+  SilcSoftaccCipher c = context;
+  SilcSoftacc sa;
+  SilcUInt32 i;
+
+  /* If key is present set it.  If it is NULL this is initialization call. */
+  if (key) {
+    SILC_SOFTACC_DEBUG(("Set key for accelerator %s %p", ops->alg_name, c));
+
+    aes_encrypt_key(key, keylen, &c->c.aes.u.enc);
+    c->key_set = TRUE;
+
+    /* Set the counters for each key stream and push them to the queue for
+       precompuptation. */
+    for (i = 0; i < c->num_key_stream; i++) {
+      memcpy(c->key_stream[i]->ctr, c->iv, SILC_AES_BLOCK);
+      silc_softacc_add_ctr(c->key_stream[i]->ctr, SILC_AES_BLOCK,
+                          i * c->cipher_blocks);
+      silc_thread_queue_push(c->queue, c->num_key_stream, c->key_stream[i],
+                            FALSE);
+    }
+
+    return TRUE;
+  }
+
+  /* Initialize the accelerator for this cipher */
+  SILC_LOG_DEBUG(("Initialize accelerator for %s %p", ops->alg_name, c));
+
+  sa = silc_global_get_var("softacc", FALSE);
+  if (!sa) {
+    SILC_LOG_ERROR(("Software accelerator not initialized"));
+    return FALSE;
+  }
+
+  /* Start the queue with sa->cipher_blocks many key streams.  One extra pipe
+     in the queue is used as a return pipe for empty key streams. */
+  c->cipher_blocks = sa->cipher_blocks;
+  c->cipher_threads = sa->cipher_threads;
+  c->num_key_stream = sa->cipher_streams;
+  c->key_stream = silc_calloc(c->num_key_stream, sizeof(*c->key_stream));
+  if (!c->key_stream)
+    return FALSE;
+  for (i = 0; i < c->num_key_stream; i++) {
+    c->key_stream[i] = silc_malloc(sizeof(**c->key_stream) +
+                                  (c->cipher_blocks * SILC_AES_BLOCK));
+    if (!c->key_stream[i])
+      return FALSE;
+    c->key_stream[i]->key_index = i;
+  }
+  c->queue = silc_thread_queue_alloc(c->num_key_stream + 1, TRUE);
+  if (!c->queue)
+    return FALSE;
+
+  /* Start the threads.  If thread starting fails, we can't accelerate the
+     cipher.  The uninit operation will clean up any started threads. */
+  for (i = 0; i < sa->cipher_threads; i++)
+    if (!silc_thread_pool_run(sa->tp, FALSE, NULL,
+                             silc_softacc_cipher_aes_thread,
+                             c, silc_softacc_cipher_aes_completion, c))
+      return FALSE;
+
+  return TRUE;
+}
+
+/* Accelerated encryption/decryption in CTR mode */
+
+SILC_CIPHER_API_ENCRYPT(softacc_cipher_aes)
+{
+  SilcSoftaccCipher c = context;
+  SilcSoftaccCipherKeyStream key;
+  SilcUInt32 pad = c->pad, block = c->cur_block;
+  SilcUInt32 blocks, cipher_blocks = c->cipher_blocks;
+  unsigned char *enc_ctr;
+
+  key = c->cur;
+  if (!key) {
+    c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
+    SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key, key->key_index));
+  }
+
+  enc_ctr = key->key + (block << 4);
+
+  /* Compute partial block */
+  if (pad < SILC_AES_BLOCK) {
+    while (len-- > 0) {
+      *dst++ = *src++ ^ enc_ctr[pad++];
+      if (pad == SILC_AES_BLOCK) {
+       enc_ctr += SILC_AES_BLOCK;
+       if (++block == cipher_blocks) {
+         /* Push the used up key stream back to the queue */
+         SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
+                             key, key->key_index));
+         silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
+
+         /* Get new key stream from queue */
+         c->cur_index = (c->cur_index + 1) % c->num_key_stream;
+         c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
+         SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key,
+                             key->key_index));
+         enc_ctr = key->key;
+         block = 0;
+       }
+       break;
+      }
+    }
+  }
+
+  /* Compute full blocks */
+  blocks = len >> 4;
+  len -= (blocks << 4);
+  while (blocks--) {
+    /* CTR mode */
+#ifndef WORDS_BIGENDIAN
+    *(SilcUInt64 *)dst = (*(SilcUInt64 *)src ^
+                         *(SilcUInt64 *)enc_ctr);
+    *(SilcUInt64 *)(dst + 8) = (*(SilcUInt64 *)(src + 8) ^
+                               *(SilcUInt64 *)(enc_ctr + 8));
+#else
+    SilcUInt64 dst_tmp, src_tmp, enc_ctr_tmp;
+
+    SILC_GET64_MSB(src_tmp, src);
+    SILC_GET64_MSB(enc_ctr_tmp, enc_ctr);
+    dst_tmp = src_tmp ^ enc_ctr_tmp;
+    SILC_PUT64_MSB(dst_tmp, dst);
+
+    SILC_GET64_MSB(src_tmp, src + 8);
+    SILC_GET64_MSB(enc_ctr_tmp, enc_ctr + 8);
+    dst_tmp = src_tmp ^ enc_ctr_tmp;
+    SILC_PUT64_MSB(dst_tmp, dst + 8);
+#endif /* !WORDS_BIGENDIAN */
+
+    src += SILC_AES_BLOCK;
+    dst += SILC_AES_BLOCK;
+    enc_ctr += SILC_AES_BLOCK;
+
+    if (++block == cipher_blocks) {
+      /* Push the used up key stream back to the queue */
+      SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
+                         key, key->key_index));
+      silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
+
+      /* Get new key stream from queue */
+      c->cur_index = (c->cur_index + 1) % c->num_key_stream;
+      c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
+      SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key, key->key_index));
+      enc_ctr = key->key;
+      block = 0;
+    }
+  }
+
+  /* Compute partial block */
+  if (len > 0) {
+    pad = 0;
+    while (len-- > 0)
+      *dst++ = *src++ ^ enc_ctr[pad++];
+  }
+
+  c->cur_block = block;
+  c->pad = pad;
+
+  return TRUE;
+}
+
+/****************************** Other ciphers *******************************/
+
+/* Thread destructor */
+
+static SILC_TASK_CALLBACK(silc_softacc_cipher_completion)
+{
+  SilcSoftaccCipher c = context;
+  int i;
+
+  /* Disconnect from key stream queue */
+  if (silc_thread_queue_disconnect(c->queue))
+    return;
+
+  silc_cipher_free(c->c.ecb);
+  for (i = 0; i < c->num_key_stream; i++)
+    silc_free(c->key_stream[i]);
+  silc_free(c->key_stream);
+  memset(c, 0, sizeof(*c));
+  silc_free(c);
+}
+
+/* Key stream computation thread */
+
+void silc_softacc_cipher_thread(SilcSchedule schedule, void *context)
+{
+  SilcSoftaccCipher c = context;
+  SilcThreadQueue queue = c->queue;
+  SilcSoftaccCipherKeyStream key = NULL;
+  SilcUInt32 i, block_len, num_key_stream = c->num_key_stream;
+  SilcUInt32 cipher_blocks = c->cipher_blocks;
+  SilcInt32 k;
+  unsigned char *enc_ctr;
+
+  SILC_SOFTACC_DEBUG(("Start CTR precomputation thread"));
+
+  block_len = silc_cipher_get_block_len(c->c.ecb);
+
+  /* Connect to the key stream queue */
+  silc_thread_queue_connect(queue);
+
+  /* Process key streams.  We wait for empty key streams to come from the
+     last pipe in the queue.  Here we precompute the key stream and put them
+     back to the queue. */
+  while (1) {
+    key = silc_thread_queue_pop(queue, num_key_stream, TRUE);
+    if (key == SILC_KEYSTREAM_STOP)
+      break;
+
+    SILC_SOFTACC_DEBUG(("Precompute key stream %p, index %d", key,
+                       key->key_index));
+
+    /* Encrypt */
+    enc_ctr = key->key;
+    for (i = 0; i < cipher_blocks; i++) {
+      for (k = block_len - 1; k >= 0; k--)
+       if (++key->ctr[k])
+         break;
+      c->c.ecb->cipher->encrypt(c->c.ecb, c->c.ecb->cipher, c->c.ecb->context,
+                               key->ctr, enc_ctr, block_len, NULL);
+      enc_ctr += block_len;
+    }
+
+    SILC_SOFTACC_DEBUG(("Precomputed key stream %p, index %d", key,
+                       key->key_index));
+
+    /* Update counter */
+    silc_softacc_add_ctr(key->ctr, block_len,
+                        (num_key_stream - 1) * cipher_blocks);
+
+    /* Put it back to queue */
+    silc_thread_queue_push(queue, key->key_index, key, FALSE);
+  }
+
+  SILC_SOFTACC_DEBUG(("End CTR precomputation thread"));
+}
+
+/* Accelerate cipher */
+
+SILC_CIPHER_API_SET_KEY(softacc_cipher)
+{
+  SilcSoftaccCipher c = context;
+  SilcSoftacc sa;
+  SilcUInt32 i;
+
+  /* If key is present set it.  If it is NULL this is initialization call. */
+  if (key) {
+    SILC_SOFTACC_DEBUG(("Set key for accelerator %s %p", ops->alg_name, c));
+
+    SILC_VERIFY(c->c.ecb && c->queue);
+
+    if (!silc_cipher_set_key(c->c.ecb, key, keylen, TRUE))
+      return FALSE;
+    c->key_set = TRUE;
+
+    /* Set the counters for each key stream and push them to the queue for
+       precompuptation. */
+    for (i = 0; i < c->num_key_stream; i++) {
+      memcpy(c->key_stream[i]->ctr, c->iv, silc_cipher_get_iv_len(c->c.ecb));
+      silc_softacc_add_ctr(c->key_stream[i]->ctr,
+                          silc_cipher_get_block_len(c->c.ecb),
+                          i * c->cipher_blocks);
+      silc_thread_queue_push(c->queue, c->num_key_stream, c->key_stream[i],
+                            FALSE);
+    }
+
+    return TRUE;
+  }
+
+  /* Initialize the accelerator for this cipher */
+  SILC_LOG_DEBUG(("Initialize accelerator for %s %p", ops->alg_name, c));
+
+  sa = silc_global_get_var("softacc", FALSE);
+  if (!sa) {
+    SILC_LOG_ERROR(("Software accelerator not initialized"));
+    return FALSE;
+  }
+
+  /* Allocate cipher in ECB mode.  It is used to encrypt the key stream. */
+  if (!silc_cipher_alloc_full(ops->alg_name, ops->key_len,
+                             SILC_CIPHER_MODE_ECB, &c->c.ecb))
+    return FALSE;
+
+  /* Start the queue with sa->cipher_blocks many key streams.  One extra pipe
+     in the queue is used as a return pipe for empty key streams. */
+  c->cipher_blocks = sa->cipher_blocks;
+  c->cipher_threads = sa->cipher_threads;
+  c->num_key_stream = sa->cipher_streams;
+  c->key_stream = silc_calloc(c->num_key_stream, sizeof(*c->key_stream));
+  if (!c->key_stream)
+    return FALSE;
+  for (i = 0; i < c->num_key_stream; i++) {
+    c->key_stream[i] = silc_malloc(sizeof(**c->key_stream) +
+                                  (c->cipher_blocks *
+                                   silc_cipher_get_block_len(c->c.ecb)));
+    if (!c->key_stream[i])
+      return FALSE;
+    c->key_stream[i]->key_index = i;
+  }
+  c->queue = silc_thread_queue_alloc(c->num_key_stream + 1, TRUE);
+  if (!c->queue)
+    return FALSE;
+
+  /* Start the threads.  If thread starting fails, we can't accelerate the
+     cipher.  The uninit operation will clean up any started threads. */
+  for (i = 0; i < sa->cipher_threads; i++)
+    if (!silc_thread_pool_run(sa->tp, FALSE, NULL, silc_softacc_cipher_thread,
+                             c, silc_softacc_cipher_completion, c))
+      return FALSE;
+
+  return TRUE;
+}
+
+/* Set IV.  Also, reset current block, discarding any remaining unused bits in
+   the current key block. */
+
+SILC_CIPHER_API_SET_IV(softacc_cipher)
+{
+  SilcSoftaccCipher c = context;
+  SilcSoftaccCipherKeyStream key;
+  SilcUInt32 i, block_len, iv_len;
+
+  block_len = silc_cipher_get_block_len(c->c.ecb);
+  iv_len = silc_cipher_get_iv_len(c->c.ecb);
+
+  if (c->pad > block_len)
+    c->pad = block_len;
+
+  /* If IV is NULL we start new block */
+  if (!iv) {
+    SILC_SOFTACC_DEBUG(("Start new block"));
+
+    if (c->pad < block_len) {
+      c->pad = block_len;
+
+      /* Start new block */
+      if (++c->cur_block == c->cipher_blocks) {
+        SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
+                           c->cur, c->cur->key_index));
+        silc_thread_queue_push(c->queue, c->num_key_stream, c->cur, FALSE);
+        c->cur_index = (c->cur_index + 1) % c->cipher_blocks;
+        c->cur_block = 0;
+        c->cur = NULL;
+      }
+    }
+  } else {
+    /* Start new IV */
+    SILC_SOFTACC_DEBUG(("Start new counter"));
+
+    memcpy(c->iv, iv, iv_len);
+
+    if (!c->key_set)
+      return;
+
+    /* Push current key stream back to queue.  We need all of them there
+       below. */
+    if (c->cur)
+      silc_thread_queue_push(c->queue, c->cur->key_index, c->cur, FALSE);
+
+    /* We must get all key streams and update them. */
+    for (i = 0; i < c->num_key_stream; i++) {
+      key = silc_thread_queue_pop(c->queue, i, TRUE);
+      memcpy(key->ctr, c->iv, iv_len);
+      silc_softacc_add_ctr(key->ctr, iv_len, i * c->cipher_blocks);
+      silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
+    }
+
+    c->cur = NULL;
+    c->cur_index = 0;
+    c->cur_block = 0;
+    c->pad = block_len;
+  }
+}
+
+SILC_CIPHER_API_ENCRYPT(softacc_cipher)
+{
+  SilcSoftaccCipher c = context;
+  SilcSoftaccCipherKeyStream key;
+  SilcUInt32 pad = c->pad, block = c->cur_block;
+  SilcUInt32 cipher_blocks = c->cipher_blocks;
+  SilcUInt32 blocks, block_len, i;
+  unsigned char *enc_ctr;
+
+  key = c->cur;
+  if (!key) {
+    c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
+    SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key, key->key_index));
+  }
+
+  block_len = c->c.ecb->cipher->block_len;
+  enc_ctr = key->key + (block * block_len);
+
+  /* Compute partial block */
+  if (pad < block_len) {
+    while (len-- > 0) {
+      *dst++ = *src++ ^ enc_ctr[pad++];
+      if (pad == block_len) {
+       enc_ctr += block_len;
+       if (++block == cipher_blocks) {
+         /* Push the used up key stream back to the queue */
+         SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
+                             key, key->key_index));
+         silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
+
+         /* Get new key stream from queue */
+         c->cur_index = (c->cur_index + 1) % c->num_key_stream;
+         c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
+         SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key,
+                             xskey->key_index));
+         enc_ctr = key->key;
+         block = 0;
+       }
+       break;
+      }
+    }
+  }
+
+  /* Compute full blocks */
+  blocks = (len / block_len);
+  len -= (blocks * block_len);
+  while (blocks--) {
+    /* CTR mode */
+#ifndef WORDS_BIGENDIAN
+    for (i = 0; i < block_len / sizeof(SilcUInt64); i++)
+      *(SilcUInt64 *)(dst + (i * sizeof(SilcUInt64))) =
+       *(SilcUInt64 *)(src + (i * sizeof(SilcUInt64))) ^
+       *(SilcUInt64 *)(enc_ctr + (i * sizeof(SilcUInt64)));
+#else
+    SilcUInt64 dst_tmp, src_tmp, enc_ctr_tmp;
+
+    for (i = 0; i < block_len / sizeof(SilcUInt64); i++) {
+      SILC_GET64_MSB(src_tmp, src + (i * sizeof(SilcUInt64)));
+      SILC_GET64_MSB(enc_ctr_tmp, enc_ctr + (i * sizeof(SilcUInt64)));
+      dst_tmp = src_tmp ^ enc_ctr_tmp;
+      SILC_PUT64_MSB(dst_tmp, dst + (i * sizeof(SilcUInt64)));
+    }
+#endif /* !WORDS_BIGENDIAN */
+
+    src += block_len;
+    dst += block_len;
+    enc_ctr += block_len;
+
+    if (++block == cipher_blocks) {
+      /* Push the used up key stream back to the queue */
+      SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
+                         key, key->key_index));
+      silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
+
+      /* Get new key stream from queue */
+      c->cur_index = (c->cur_index + 1) % c->num_key_stream;
+      c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
+      SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key,
+                         key->key_index));
+      enc_ctr = key->key;
+      block = 0;
+    }
+  }
+
+  /* Compute partial block */
+  if (len > 0) {
+    pad = 0;
+    while (len-- > 0)
+      *dst++ = *src++ ^ enc_ctr[pad++];
+  }
+
+  c->cur_block = block;
+  c->pad = pad;
+
+  return TRUE;
+}
+
+/* Return accelerator cipher context */
+
+SILC_CIPHER_API_INIT(softacc_cipher)
+{
+  SilcSoftaccCipher c = silc_calloc(1, sizeof(*c));
+
+  if (!c)
+    return NULL;
+
+  c->pad = 16;
+
+  return c;
+}
+
+/* Uninitialize the cipher accelerator */
+
+SILC_CIPHER_API_UNINIT(softacc_cipher)
+{
+  SilcSoftaccCipher c = context;
+  int i;
+
+  /* Stop threads */
+  if (c->queue) {
+    for (i = 0; i < c->cipher_threads; i++)
+      silc_thread_queue_push(c->queue, c->num_key_stream,
+                            SILC_KEYSTREAM_STOP, FALSE);
+
+    /* Disconnect from key stream queue */
+    if (silc_thread_queue_disconnect(c->queue))
+      return;
+  }
+
+  silc_free(c->key_stream);
+  memset(c, 0, sizeof(*c));
+  silc_free(c);
+}
diff --git a/lib/silcacc/softacc_i.h b/lib/silcacc/softacc_i.h
new file mode 100644 (file)
index 0000000..b0e9c17
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+
+  softacc_i.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 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.
+
+*/
+
+#ifndef SOFTACC_I_H
+#define SOFTACC_I_H
+
+#if SILC_SOFTACC_DEBUG_ON == 1
+#define SILC_SOFTACC_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
+#else
+#define SILC_SOFTACC_DEBUG(fmt)
+#endif /* SILC_SOFTACC_DEBUG_ON == 1 */
+
+/* Defaults */
+#define SILC_SOFTACC_MIN_THREADS 0
+#define SILC_SOFTACC_MAX_THREADS 4
+#define SILC_SOFTACC_CIPHER_THREADS 2
+#define SILC_SOFTACC_CIPHER_BLOCKS 4096
+#define SILC_SOFTACC_CIPHER_STREAMS (SILC_SOFTACC_CIPHER_THREADS * 2)
+
+/* Software accelerator context */
+typedef struct {
+  SilcSchedule schedule;                /* Scheduler */
+  SilcThreadPool tp;                    /* The thread pool */
+
+  /* Options */
+  SilcUInt32 min_threads;
+  SilcUInt32 max_threads;
+  SilcUInt32 cipher_threads;
+  SilcUInt32 cipher_blocks;
+  SilcUInt32 cipher_streams;
+} *SilcSoftacc;
+
+/* Accelerator API */
+SilcBool silc_softacc_init(SilcSchedule schedule, va_list va);
+SilcBool silc_softacc_uninit(void);
+
+#ifdef SILC_DIST_SOFTACC_PKCS
+extern const SilcPKCSAlgorithm softacc_pkcs[];
+
+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 /* SILC_DIST_SOFTACC_PKCS */
+
+#ifdef SILC_DIST_SOFTACC_CIPHER
+extern const SilcCipherObject softacc_cipher[];
+
+SILC_CIPHER_API_SET_KEY(softacc_cipher_aes);
+SILC_CIPHER_API_ENCRYPT(softacc_cipher_aes);
+SILC_CIPHER_API_SET_IV(softacc_cipher_aes);
+
+SILC_CIPHER_API_SET_KEY(softacc_cipher);
+SILC_CIPHER_API_SET_IV(softacc_cipher);
+SILC_CIPHER_API_ENCRYPT(softacc_cipher);
+SILC_CIPHER_API_INIT(softacc_cipher);
+SILC_CIPHER_API_UNINIT(softacc_cipher);
+#endif /* SILC_DIST_SOFTACC_CIPHER */
+
+#endif /* SOFTACC_I_H */
diff --git a/lib/silcacc/softacc_pkcs.c b/lib/silcacc/softacc_pkcs.c
new file mode 100644 (file)
index 0000000..a3e9f6a
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+
+  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;
+}
index b431654f57824b6ea70870ca8dae529b356a7a03..8ceace81c9d1bce407b82db1edc108c1076b1004 100644 (file)
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
-bin_PROGRAMS = test_softacc
+bin_PROGRAMS =         test_softacc                    \
+               test_softacc_cipher             \
+               test_softacc_cipher2
 
 test_softacc_SOURCES = test_softacc.c
+test_softacc_cipher_SOURCES = test_softacc_cipher.c
+test_softacc_cipher2_SOURCES = test_softacc_cipher2.c
 
 LIBS = $(SILC_COMMON_LIBS)
-LDADD = -L.. -L../.. -lsilc -lsilcacc
+LDADD = -L.. -L../.. -lsct
 
 include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcacc/tests/test_softacc_cipher.c b/lib/silcacc/tests/test_softacc_cipher.c
new file mode 100644 (file)
index 0000000..93946a4
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+
+  test_softacc_cipher.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 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"
+
+#define ENC_LEN 0x00100000     /* enc data len (at least) */
+#define ENC_ROUND 512          /* enc rounds (at least) */
+#define ENC_MIN_TIME 15.0        /* seconds to run the test (at least) */
+
+SilcTimerStruct timer;
+SilcCipher cipher, acc_cipher;
+
+int main(int argc, char **argv)
+{
+  SilcUInt64 sec;
+  SilcUInt32 usec;
+  double totsec;
+  unsigned char *data;
+  SilcUInt32 rounds;
+  SilcUInt32 i, k;
+
+  silc_runtime_init();
+  silc_crypto_init(NULL);
+
+#if 0
+  silc_log_debug(TRUE);
+  silc_log_quick(TRUE);
+  silc_log_debug_hexdump(TRUE);
+  silc_log_set_debug_string("*acc*,*thread*");
+#endif
+
+  if (!silc_acc_init(SILC_SOFTACC, (void *)0x01, "min_threads", 2,
+                    "max_threads", 8, NULL))
+    exit(1);
+
+  data = malloc(ENC_LEN * sizeof(*data));
+  if (!data)
+    exit(1);
+
+  for (i = 0; i < ENC_LEN; i++)
+    data[i] = i % 255;
+
+  silc_timer_synchronize(&timer);
+
+  for (i = 0; silc_default_ciphers[i].name; i++) {
+    if (!silc_cipher_alloc(silc_default_ciphers[i].name, &cipher)) {
+      fprintf(stderr, "Error allocating %s\n", silc_default_ciphers[i].name);
+      exit(1);
+    }
+
+    acc_cipher = silc_acc_cipher(SILC_SOFTACC, cipher);
+    if (!acc_cipher)
+      continue;
+
+    silc_cipher_set_iv(acc_cipher, data);
+    silc_cipher_set_key(acc_cipher, data, silc_cipher_get_key_len(cipher),
+                       TRUE);
+    sleep(1);
+
+    rounds = ENC_ROUND;
+
+  retry:
+    silc_timer_start(&timer);
+    for (k = 0; k < rounds; k++)
+      silc_cipher_encrypt(acc_cipher, data, data, ENC_LEN, NULL);
+    silc_timer_stop(&timer);
+
+    silc_timer_value(&timer, &sec, &usec);
+    totsec = (double)sec;
+    totsec += ((double)usec / (double)((double)1000 * (double)1000));
+    if (totsec < ENC_MIN_TIME) {
+      rounds += rounds;
+      goto retry;
+    }
+
+    silc_cipher_free(acc_cipher);
+    silc_cipher_free(cipher);
+
+    sleep(1);
+    printf("%s:\t%.2f KB (%.2f MB, %.2f Mbit) / sec (total %.3f secs)\n",
+          silc_default_ciphers[i].name,
+          (((double)((double)ENC_LEN * (double)rounds) / 1024.0) / totsec),
+          (((double)((double)ENC_LEN * (double)rounds) / (1024.0 *
+                                                          1024.0)) / totsec),
+          ((((double)((double)ENC_LEN * (double)rounds) / 1024.0)
+            / 128.0) / totsec),
+          totsec);
+  }
+
+  silc_acc_uninit(SILC_SOFTACC);
+
+  silc_crypto_uninit();
+  silc_runtime_uninit();
+
+  return 0;
+}
diff --git a/lib/silcacc/tests/test_softacc_cipher2.c b/lib/silcacc/tests/test_softacc_cipher2.c
new file mode 100644 (file)
index 0000000..416423a
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+
+  test_softacc_cipher2.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 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.
+
+*/
+
+/* Test that the software accelerated cipher encrypts and decrypts
+   correctly. */
+
+#include "silccrypto.h"
+#include "softacc.h"
+
+#define ENC_LEN 799            /* enc data len */
+#define ENC_ROUND 10000                /* enc rounds */
+
+SilcCipher enc_cipher, enc_acc_cipher;
+SilcCipher dec_cipher, dec_acc_cipher;
+
+int main(int argc, char **argv)
+{
+  SilcBool success = FALSE;
+  unsigned char *data, iv[SILC_CIPHER_MAX_IV_SIZE];
+  SilcUInt32 i, k;
+
+  silc_runtime_init();
+  silc_crypto_init(NULL);
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_set_debug_string("*acc*,*cipher*,*twofish*");
+  }
+
+  if (!silc_acc_init(SILC_SOFTACC, (void *)0x01, "min_threads", 2,
+                    "max_threads", 8, NULL))
+    exit(1);
+
+  data = malloc(ENC_LEN * sizeof(*data));
+  if (!data)
+    exit(1);
+
+  /* Plaintext */
+  for (i = 0; i < ENC_LEN; i++)
+    data[i] = i % 255;
+  SILC_LOG_HEXDUMP(("data"), data, ENC_LEN);
+
+  /* IV */
+  for (i = 0; i < SILC_CIPHER_MAX_IV_SIZE; i++)
+    iv[i] = i % 255;
+
+  SILC_LOG_HEXDUMP(("IV"), iv, SILC_CIPHER_MAX_IV_SIZE);
+
+  for (i = 0; silc_default_ciphers[i].name; i++) {
+    if (!silc_cipher_alloc(silc_default_ciphers[i].name, &enc_cipher)) {
+      fprintf(stderr, "Error allocating %s\n", silc_default_ciphers[i].name);
+      exit(1);
+    }
+    if (!silc_cipher_alloc(silc_default_ciphers[i].name, &dec_cipher)) {
+      fprintf(stderr, "Error allocating %s\n", silc_default_ciphers[i].name);
+      exit(1);
+    }
+
+    enc_acc_cipher = silc_acc_cipher(SILC_SOFTACC, enc_cipher);
+    if (!enc_acc_cipher)
+      continue;
+    dec_acc_cipher = silc_acc_cipher(SILC_SOFTACC, dec_cipher);
+    if (!dec_acc_cipher)
+      continue;
+
+    SILC_LOG_DEBUG(("Allocated cipher %s", silc_default_ciphers[i].name));
+
+    SILC_LOG_DEBUG(("Set key"));
+    silc_cipher_set_key(enc_acc_cipher, data,
+                       silc_cipher_get_key_len(enc_cipher),
+                       TRUE);
+    silc_cipher_set_key(dec_acc_cipher, data,
+                       silc_cipher_get_key_len(dec_cipher),
+                       FALSE);
+
+
+    SILC_LOG_DEBUG(("Set IV"));
+    silc_cipher_set_iv(enc_acc_cipher, iv);
+
+    SILC_LOG_DEBUG(("Encrypt with accelerated cipher"));
+    for (k = 0; k < ENC_ROUND; k++)
+      silc_cipher_encrypt(enc_acc_cipher, data, data, ENC_LEN, NULL);
+    SILC_LOG_HEXDUMP(("data"), data, ENC_LEN);
+
+    SILC_LOG_DEBUG(("Set IV"));
+    silc_cipher_set_iv(dec_cipher, iv);
+
+    SILC_LOG_DEBUG(("Decrypt with associated cipher"));
+    for (k = 0; k < ENC_ROUND; k++)
+      silc_cipher_decrypt(dec_cipher, data, data, ENC_LEN, NULL);
+    SILC_LOG_HEXDUMP(("data"), data, ENC_LEN);
+
+    /* Verify */
+    SILC_LOG_DEBUG(("Verify"));
+    for (k = 0; k < ENC_LEN; k++)
+      if (data[k] != k % 255)
+       goto err;
+    SILC_LOG_DEBUG(("Ok"));
+
+
+    SILC_LOG_DEBUG(("Set IV"));
+    silc_cipher_set_iv(enc_cipher, iv);
+
+    SILC_LOG_DEBUG(("Encrypt with associated cipher"));
+    for (k = 0; k < ENC_ROUND; k++)
+      silc_cipher_encrypt(enc_cipher, data, data, ENC_LEN, NULL);
+    SILC_LOG_HEXDUMP(("data"), data, ENC_LEN);
+
+    SILC_LOG_DEBUG(("Set IV"));
+    silc_cipher_set_iv(dec_acc_cipher, iv);
+
+    SILC_LOG_DEBUG(("Decrypt with accelerated cipher"));
+    for (k = 0; k < ENC_ROUND; k++)
+      silc_cipher_decrypt(dec_acc_cipher, data, data, ENC_LEN, NULL);
+    SILC_LOG_HEXDUMP(("data"), data, ENC_LEN);
+
+    /* Verify */
+    SILC_LOG_DEBUG(("Verify"));
+    for (k = 0; k < ENC_LEN; k++)
+      if (data[k] != k % 255)
+       goto err;
+    SILC_LOG_DEBUG(("Ok"));
+
+
+    SILC_LOG_DEBUG(("Set IV"));
+    silc_cipher_set_iv(enc_acc_cipher, iv);
+
+    SILC_LOG_DEBUG(("Encrypt with accelerated cipher"));
+    for (k = 0; k < ENC_ROUND; k++)
+      silc_cipher_encrypt(enc_acc_cipher, data, data, ENC_LEN, NULL);
+    SILC_LOG_HEXDUMP(("data"), data, ENC_LEN);
+
+    SILC_LOG_DEBUG(("Set IV"));
+    silc_cipher_set_iv(dec_acc_cipher, iv);
+
+    SILC_LOG_DEBUG(("Decrypt with accelerated cipher"));
+    for (k = 0; k < ENC_ROUND; k++)
+      silc_cipher_decrypt(dec_acc_cipher, data, data, ENC_LEN, NULL);
+    SILC_LOG_HEXDUMP(("data"), data, ENC_LEN);
+
+    /* Verify */
+    SILC_LOG_DEBUG(("Verify"));
+    for (k = 0; k < ENC_LEN; k++)
+      if (data[k] != k % 255)
+       goto err;
+    SILC_LOG_DEBUG(("Ok"));
+
+
+    SILC_LOG_DEBUG(("Set IV"));
+    silc_cipher_set_iv(enc_cipher, iv);
+
+    SILC_LOG_DEBUG(("Encrypt with associated cipher"));
+    for (k = 0; k < ENC_ROUND; k++)
+      silc_cipher_encrypt(enc_cipher, data, data, ENC_LEN, NULL);
+    SILC_LOG_HEXDUMP(("data"), data, ENC_LEN);
+
+    SILC_LOG_DEBUG(("Set IV"));
+    silc_cipher_set_iv(dec_cipher, iv);
+
+    SILC_LOG_DEBUG(("Decrypt with associated cipher"));
+    for (k = 0; k < ENC_ROUND; k++)
+      silc_cipher_decrypt(dec_cipher, data, data, ENC_LEN, NULL);
+    SILC_LOG_HEXDUMP(("data"), data, ENC_LEN);
+
+    /* Verify */
+    SILC_LOG_DEBUG(("Verify"));
+    for (k = 0; k < ENC_LEN; k++)
+      if (data[k] != k % 255)
+       goto err;
+    SILC_LOG_DEBUG(("Ok"));
+
+
+    silc_cipher_free(enc_acc_cipher);
+    silc_cipher_free(enc_cipher);
+    silc_cipher_free(dec_acc_cipher);
+    silc_cipher_free(dec_cipher);
+  }
+
+  silc_acc_uninit(SILC_SOFTACC);
+
+  success = TRUE;
+
+ err:
+  SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+  fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+  silc_crypto_uninit();
+  silc_runtime_uninit();
+
+  return !success;
+}