Added cipher acceleration to SILC Accelerator. Added cipher softacc.
[crypto.git] / lib / silcacc / silcacc_cipher.c
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;
+}