+/*
+
+ 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;
+}