Added cipher acceleration to SILC Accelerator. Added cipher softacc.
[crypto.git] / lib / silcacc / silcacc_cipher.c
1 /*
2
3   silcacc_cipher.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2008 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silccrypto.h"
21
22 /************************** Types and definitions ***************************/
23
24 SILC_CIPHER_API_SET_KEY(acc_cipher);
25 SILC_CIPHER_API_SET_IV(acc_cipher);
26 SILC_CIPHER_API_ENCRYPT(acc_cipher);
27 SILC_CIPHER_API_DECRYPT(acc_cipher);
28 SILC_CIPHER_API_INIT(acc_cipher);
29 SILC_CIPHER_API_UNINIT(acc_cipher);
30
31 /* Accelerated cipher */
32 typedef struct SilcAcceleratorCipherStruct {
33   SilcCipher cipher;            /* Associated cipher */
34   SilcCipher acc_cipher;        /* Accelerator cipher */
35 } *SilcAcceleratorCipher;
36
37 /************************** Accelerator Cipher API **************************/
38
39 /* The Cipher API for the accelerated cipher is simply a wrapper.  It
40    calls the SILC Cipher API for the accelerator cipher. */
41
42 const SilcCipherObject silc_acc_ciph =
43 {
44   "silc_acc_cipher",
45   "silc_acc_cipher",
46
47   silc_acc_cipher_set_key,
48   silc_acc_cipher_set_iv,
49   silc_acc_cipher_encrypt,
50   silc_acc_cipher_decrypt,
51   silc_acc_cipher_init,
52   silc_acc_cipher_uninit,
53
54   0, 0, 0, 0
55 };
56
57 SILC_CIPHER_API_SET_KEY(acc_cipher)
58 {
59   SilcAcceleratorCipher c = context;
60
61   /* Set key for the associated cipher too */
62   silc_cipher_set_key(c->cipher, key, keylen, encryption);
63
64   /* Set key for accelerator */
65   return silc_cipher_set_key(c->acc_cipher, key, keylen, encryption);
66 }
67
68 SILC_CIPHER_API_SET_IV(acc_cipher)
69 {
70   SilcAcceleratorCipher c = context;
71
72   /* Set IV for the associated cipher too */
73   silc_cipher_set_iv(c->cipher, iv);
74
75   /* Set IV for accelerator */
76   silc_cipher_set_iv(c->acc_cipher, iv);
77 }
78
79 SILC_CIPHER_API_ENCRYPT(acc_cipher)
80 {
81   SilcAcceleratorCipher c = context;
82   return silc_cipher_encrypt(c->acc_cipher, src, dst, len, iv);
83 }
84
85 SILC_CIPHER_API_DECRYPT(acc_cipher)
86 {
87   SilcAcceleratorCipher c = context;
88   return silc_cipher_decrypt(c->acc_cipher, src, dst, len, iv);
89 }
90
91 SILC_CIPHER_API_INIT(acc_cipher)
92 {
93   /* This operation is never called */
94   return NULL;
95 }
96
97 SILC_CIPHER_API_UNINIT(acc_cipher)
98 {
99   SilcAcceleratorCipher c = context;
100   SilcCipherObject *acc_ops = c->acc_cipher->cipher;
101
102   /* Free the accelerator cipher and its operations we allocated earlier. */
103   silc_cipher_free(c->acc_cipher);
104   silc_free(acc_ops);
105
106   /* Free our operations too */
107   silc_free(ops);
108 }
109
110 /*************************** SILC Accelerator API ***************************/
111
112 /* Accelerate cipher */
113
114 SilcCipher silc_acc_cipher(SilcAccelerator acc, SilcCipher cipher)
115 {
116   SilcCipher c;
117   SilcAcceleratorCipher acc_cipher;
118   const SilcCipherObject *alg;
119   int i;
120
121   if (!acc || !cipher)
122     return NULL;
123
124   SILC_LOG_DEBUG(("Accelerate cipher %p with accelerator %s",
125                   cipher, acc->name));
126
127   if (!acc->cipher) {
128     SILC_LOG_ERROR(("Accelerator '%s' does not support cipher acceleration ",
129                     acc->name));
130     return NULL;
131   }
132
133   if (silc_acc_get_cipher(NULL, cipher)) {
134     SILC_LOG_DEBUG(("Cipher %p is already accelerated", cipher));
135     return NULL;
136   }
137
138   /* Check that accelerator supports this cipher algorithm */
139   alg = cipher->cipher;
140   for (i = 0; acc->cipher[i].alg_name; i++) {
141     if ((!strcmp(acc->cipher[i].alg_name, alg->alg_name) ||
142          !strcmp(acc->cipher[i].alg_name, "any")) &&
143         (acc->cipher[i].mode == alg->mode ||
144          acc->cipher[i].mode == 0) &&
145         (acc->cipher[i].key_len == alg->key_len ||
146          acc->cipher[i].key_len == 0)) {
147       alg = NULL;
148       break;
149     }
150   }
151   if (alg) {
152     SILC_LOG_DEBUG(("Accelerator %s does not support %s (mode %d) "
153                     "acceleration", acc->name, alg->name, alg->mode));
154     return NULL;
155   }
156
157   /* Allocate cipher context for the SILC Cipher API */
158   c = silc_calloc(1, sizeof(*c));
159   if (!c)
160     return NULL;
161
162   /* Allocate cipher operations */
163   c->cipher = silc_calloc(1, sizeof(SilcCipherObject));
164   if (!c->cipher) {
165     silc_free(c);
166     return NULL;
167   }
168   *c->cipher = silc_acc_ciph;
169
170   /* Allocate cipher context */
171   c->context = acc_cipher = silc_calloc(1, sizeof(*acc_cipher));
172   if (!acc_cipher) {
173     silc_free(c->cipher);
174     silc_free(c);
175     return NULL;
176   }
177   acc_cipher->cipher = cipher;
178
179   /* Allocate the actual algorithm accelerator. */
180   acc_cipher->acc_cipher = silc_calloc(1, sizeof(*acc_cipher->acc_cipher));
181   if (!acc_cipher->acc_cipher) {
182     silc_free(c->cipher);
183     silc_free(c);
184     silc_free(acc_cipher);
185   }
186
187   /* Initialize the algorithm accelerator */
188   acc_cipher->acc_cipher->context =
189     acc->cipher[i].init((struct SilcCipherObjectStruct *)&acc->cipher[i]);
190   if (!acc_cipher->acc_cipher->context) {
191     silc_free(c->cipher);
192     silc_free(c);
193     silc_free(acc_cipher->acc_cipher);
194     silc_free(acc_cipher);
195     return NULL;
196   }
197
198   /* Allocate algorithm accelerator operations */
199   acc_cipher->acc_cipher->cipher = silc_calloc(1, sizeof(SilcCipherObject));
200   if (!acc_cipher->acc_cipher->cipher) {
201     silc_free(c->cipher);
202     silc_free(c);
203     silc_free(acc_cipher->acc_cipher->context);
204     silc_free(acc_cipher->acc_cipher);
205     silc_free(acc_cipher);
206     return NULL;
207   }
208
209   /* Set algorithm accelerator operations.  They are copied from the
210      accelerator, but algorithm specific things come from associated
211      cipher.  This way accelerators get the associated cipher details. */
212   *acc_cipher->acc_cipher->cipher = acc->cipher[i];
213   acc_cipher->acc_cipher->cipher->alg_name =
214     (char *)silc_cipher_get_alg_name(cipher);
215   acc_cipher->acc_cipher->cipher->key_len = silc_cipher_get_key_len(cipher);
216   acc_cipher->acc_cipher->cipher->block_len =
217     silc_cipher_get_block_len(cipher);
218   acc_cipher->acc_cipher->cipher->iv_len = silc_cipher_get_iv_len(cipher);
219
220   /* Set for the accelerator cipher too */
221   c->cipher->key_len = silc_cipher_get_key_len(cipher);
222   c->cipher->block_len = silc_cipher_get_block_len(cipher);
223   c->cipher->iv_len = silc_cipher_get_iv_len(cipher);
224
225   /* Start the accelerator.  The accelerator is started by setting key
226      with NULL key. */
227   if (!silc_cipher_set_key(acc_cipher->acc_cipher, NULL, 0, FALSE)) {
228     SilcCipherObject *ops = acc_cipher->acc_cipher->cipher;
229     silc_cipher_free(acc_cipher->acc_cipher);
230     silc_free(ops);
231     silc_free(c->cipher);
232     silc_free(c);
233     return NULL;
234   }
235
236   SILC_LOG_DEBUG(("New accelerated cipher %p", c));
237
238   return c;
239 }
240
241 /* Return underlaying cipher from accelerated cipher. */
242
243 SilcCipher silc_acc_get_cipher(SilcAccelerator acc, SilcCipher cipher)
244 {
245   SilcAcceleratorCipher acc_cipher;
246
247   if (!cipher || cipher->cipher != &silc_acc_ciph)
248     return NULL;
249
250   acc_cipher = cipher->context;
251
252   return acc_cipher->cipher;
253 }