Added SILC Accelerator Library.
[silc.git] / lib / silcacc / softacc.c
1 /*
2
3   softacc.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2007 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 "silc.h"
21 #include "softacc.h"
22
23 /* Software accelerator is a thread-pool system where public key and private
24    key operations are executed in threads for the purpose of off-loading and
25    balancing the computations across multiple processors. */
26
27 #define SILC_SOFTACC_MIN_THREADS 0
28 #define SILC_SOFTACC_MAX_THREADS 4
29
30 /************************** Types and definitions ***************************/
31
32 /* Software accelerator PKCS algorithm operations */
33 const SilcPKCSAlgorithm softacc_pkcs[] =
34 {
35   {
36     "any", "any", NULL, NULL,
37     silc_softacc_acc_public_key,
38     NULL, NULL, NULL, NULL,
39     silc_softacc_free_public_key,
40     silc_softacc_acc_private_key,
41     NULL, NULL,
42     silc_softacc_free_private_key,
43     silc_softacc_encrypt,
44     silc_softacc_decrypt,
45     silc_softacc_sign,
46     silc_softacc_verify,
47   },
48
49   {
50     NULL, NULL, NULL, NULL,
51     NULL, NULL, NULL, NULL,
52     NULL, NULL, NULL, NULL,
53     NULL, NULL
54   }
55 };
56
57 /* Software accelerator operations */
58 const SilcAcceleratorStruct softacc =
59 {
60   "softacc", silc_softacc_init, silc_softacc_uninit, softacc_pkcs
61 };
62
63 /* Software accelerator public key */
64 typedef struct {
65   SilcPublicKey key;                     /* Accelerated public key */
66 } *SilcSoftaccPublicKey;
67
68 /* Software accelerator private key */
69 typedef struct {
70   SilcPrivateKey key;                    /* Accelerated private key */
71 } *SilcSoftaccPrivateKey;
72
73 /* Execution types */
74 typedef enum {
75   SILC_SOFTACC_ENCRYPT,
76   SILC_SOFTACC_DECRYPT,
77   SILC_SOFTACC_SIGN,
78   SILC_SOFTACC_VERIFY,
79 } SilcSoftaccType;
80
81 /* Executor context */
82 typedef struct {
83   SilcStack stack;                       /* Executor stack */
84   void *context;                         /* Callback context */
85   SilcSoftaccType type;                  /* Execution type */
86   SilcAsyncOperationStruct op;           /* Operation for aborting */
87
88   unsigned char *src;                    /* Source data */
89   unsigned char *data;                   /* More source data */
90   SilcUInt32 src_len;
91   SilcUInt32 data_len;
92   SilcHash hash;                         /* Hash function to use */
93   SilcRng rng;                           /* RNG, may be NULL */
94
95   union {
96     SilcPublicKey public_key;
97     SilcPrivateKey private_key;
98   } key;
99
100   union {
101     SilcPKCSEncryptCb encrypt_cb;
102     SilcPKCSDecryptCb decrypt_cb;
103     SilcPKCSSignCb sign_cb;
104     SilcPKCSVerifyCb verify_cb;
105   } cb;
106
107   unsigned char *result_data;
108   SilcUInt32 result_len;
109
110   unsigned int result       : 1;
111   unsigned int compute_hash : 1;
112   unsigned int aborted      : 1;
113 } *SilcSoftaccExec;
114
115 /* Software accelerator context */
116 typedef struct {
117   SilcSchedule schedule;                 /* Scheduler */
118   SilcThreadPool tp;                     /* The thread pool */
119 } *SilcSoftacc;
120
121 SilcSoftacc sa = NULL;                   /* The accelerator */
122
123 /***************************** Accelerator API ******************************/
124
125 /* Initialize software accelerator.  Optional initialization parameters:
126
127    min_threads     number        Minimum number of threads (default 0)
128    max_thread      number        Maximum number of threads (default 4)
129
130    Eg. silc_acc_init(softacc, "min_threads", 2, "max_threads", 8, NULL);
131
132 */
133
134 SilcBool silc_softacc_init(SilcSchedule schedule, va_list va)
135 {
136   SilcUInt32 min_threads = SILC_SOFTACC_MIN_THREADS;
137   SilcUInt32 max_threads = SILC_SOFTACC_MAX_THREADS;
138   char *opt;
139
140   if (!schedule)
141     return FALSE;
142
143   /* If already initialized, uninitialize first. */
144   if (sa)
145     silc_softacc_uninit();
146
147   /* Get options */
148   while ((opt = va_arg(va, char *))) {
149     if (!strcmp(opt, "min_threads"))
150       min_threads = va_arg(va, SilcUInt32);
151     else if (!strcmp(opt, "max_threads"))
152       max_threads = va_arg(va, SilcUInt32);
153   }
154
155   SILC_LOG_DEBUG(("Initialize software accelerator, min_threads %d, "
156                   "max_threads %d", min_threads, max_threads));
157
158   sa = silc_calloc(1, sizeof(*sa));
159   if (!sa)
160     return FALSE;
161
162   sa->schedule = schedule;
163
164   /* Start the thread pool */
165   sa->tp = silc_thread_pool_alloc(NULL, min_threads, max_threads, TRUE);
166   if (!sa->tp) {
167     silc_free(sa);
168     return FALSE;
169   }
170
171   return TRUE;
172 }
173
174 /* Uninitialize */
175
176 SilcBool silc_softacc_uninit(void)
177 {
178   if (!sa)
179     return FALSE;
180
181   SILC_LOG_DEBUG(("Uninitialize software accelerator"));
182
183   silc_thread_pool_free(sa->tp, TRUE);
184   silc_free(sa);
185   sa = NULL;
186
187   return TRUE;
188 }
189
190 /****************************** PKCS ALG API ********************************/
191
192 /* Abort operation */
193
194 void silc_softacc_abort(SilcAsyncOperation op, void *context)
195 {
196   SilcSoftaccExec e = context;
197   e->aborted = TRUE;
198 }
199
200 /* Accelerator completion, executed in main thread. */
201
202 SILC_TASK_CALLBACK(silc_softacc_completion)
203 {
204   SilcSoftaccExec e = context;
205   SilcStack stack = e->stack;
206
207   /* At the latest, abort is catched here in the main thread.  Don't
208      deliver callback if we were aborted */
209   if (e->aborted)
210     goto out;
211
212   SILC_LOG_DEBUG(("Call completion, result=%s", e->result ? "Ok" : "failed"));
213
214   /* Call completion callback */
215   switch (e->type) {
216   case SILC_SOFTACC_ENCRYPT:
217     e->cb.encrypt_cb(e->result, e->result_data, e->result_len, e->context);
218     break;
219
220   case SILC_SOFTACC_DECRYPT:
221     e->cb.decrypt_cb(e->result, e->result_data, e->result_len, e->context);
222     break;
223
224   case SILC_SOFTACC_SIGN:
225     e->cb.sign_cb(e->result, e->result_data, e->result_len, e->context);
226     break;
227
228   case SILC_SOFTACC_VERIFY:
229     e->cb.verify_cb(e->result, e->context);
230     break;
231   }
232
233  out:
234   silc_sfree(stack, e->src);
235   silc_sfree(stack, e->data);
236   silc_sfree(stack, e);
237   silc_stack_free(stack);
238 }
239
240 /* Callback for encrypt, decrypt and signature */
241
242 void silc_softacc_data_cb(SilcBool success, const unsigned char *data,
243                           SilcUInt32 data_len, void *context)
244 {
245   SilcSoftaccExec e = context;
246   SilcStack stack = e->stack;
247
248   /* Pop e->src and e->data from memory */
249   silc_stack_pop(stack);
250
251   if (success)
252     e->result_data = silc_smemdup(stack, data, data_len);
253   e->result_len = data_len;
254   e->result = success;
255 }
256
257 /* Verification callback */
258
259 void silc_softacc_verify_cb(SilcBool success, void *context)
260 {
261   SilcSoftaccExec e = context;
262   SilcStack stack = e->stack;
263
264   silc_stack_pop(stack);
265   e->result = success;
266 }
267
268 /* Accelerator thread */
269
270 void silc_softacc_thread(SilcSchedule schedule, void *context)
271 {
272   SilcSoftaccExec e = context;
273
274   if (e->aborted)
275     return;
276
277   SILC_LOG_DEBUG(("Execute type %d", e->type));
278
279   /* Call the operation */
280   switch (e->type) {
281   case SILC_SOFTACC_ENCRYPT:
282     silc_pkcs_encrypt(e->key.public_key, e->src, e->src_len, e->rng,
283                       silc_softacc_data_cb, e);
284     break;
285
286   case SILC_SOFTACC_DECRYPT:
287     silc_pkcs_decrypt(e->key.private_key, e->src, e->src_len,
288                       silc_softacc_data_cb, e);
289     break;
290
291   case SILC_SOFTACC_SIGN:
292     silc_pkcs_sign(e->key.private_key, e->src, e->src_len, e->compute_hash,
293                    e->hash, silc_softacc_data_cb, e);
294     break;
295
296   case SILC_SOFTACC_VERIFY:
297     silc_pkcs_verify(e->key.public_key, e->src, e->src_len, e->data,
298                      e->data_len, e->hash, silc_softacc_verify_cb, e);
299     break;
300   }
301 }
302
303 /* Accelerate public key */
304
305 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_softacc_acc_public_key)
306 {
307   SilcSoftaccPublicKey pubkey;
308
309   if (!sa) {
310     SILC_LOG_ERROR(("Software accelerator not initialized"));
311     return FALSE;
312   }
313
314   pubkey = silc_calloc(1, sizeof(*pubkey));
315   if (!pubkey)
316     return FALSE;
317   pubkey->key = key;
318
319   *ret_public_key = pubkey;
320
321   return TRUE;
322 }
323
324 /* Accelerate private key */
325
326 SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_softacc_acc_private_key)
327 {
328   SilcSoftaccPrivateKey privkey;
329
330   if (!sa) {
331     SILC_LOG_ERROR(("Software accelerator not initialized"));
332     return FALSE;
333   }
334
335   privkey = silc_calloc(1, sizeof(*privkey));
336   if (!privkey)
337     return FALSE;
338   privkey->key = key;
339
340   *ret_private_key = privkey;
341
342   return TRUE;
343 }
344
345 /* Free public key */
346
347 SILC_PKCS_ALG_PUBLIC_KEY_FREE(silc_softacc_free_public_key)
348 {
349   silc_free(public_key);
350 }
351
352 /* Free private key */
353
354 SILC_PKCS_ALG_PRIVATE_KEY_FREE(silc_softacc_free_private_key)
355 {
356   silc_free(private_key);
357 }
358
359 /* Accelerated encrypt */
360
361 SILC_PKCS_ALG_ENCRYPT(silc_softacc_encrypt)
362 {
363   SilcSoftaccPublicKey pubkey = public_key;
364   SilcStack stack;
365   SilcSoftaccExec e;
366
367   SILC_LOG_DEBUG(("Encrypt"));
368
369   if (!sa) {
370     SILC_LOG_ERROR(("Software accelerator not initialized"));
371     encrypt_cb(FALSE, NULL, 0, context);
372     return NULL;
373   }
374
375   stack = silc_stack_alloc(2048, silc_crypto_stack());
376
377   e = silc_scalloc(stack, 1, sizeof(*e));
378   if (!e) {
379     silc_stack_free(stack);
380     encrypt_cb(FALSE, NULL, 0, context);
381     return NULL;
382   }
383
384   silc_stack_push(stack, NULL);
385
386   e->stack = stack;
387   e->type = SILC_SOFTACC_ENCRYPT;
388   e->src = silc_smemdup(stack, src, src_len);
389   e->src_len = src_len;
390   e->rng = rng;
391   e->key.public_key = pubkey->key;
392   e->cb.encrypt_cb = encrypt_cb;
393   e->context = context;
394   silc_async_init(&e->op, silc_softacc_abort, NULL, e);
395
396   /* Run */
397   silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e,
398                        silc_softacc_completion, e);
399
400   return &e->op;
401 }
402
403 /* Acceleted decrypt */
404
405 SILC_PKCS_ALG_DECRYPT(silc_softacc_decrypt)
406 {
407   SilcSoftaccPrivateKey privkey = private_key;
408   SilcStack stack;
409   SilcSoftaccExec e;
410
411   SILC_LOG_DEBUG(("Decrypt"));
412
413   if (!sa) {
414     SILC_LOG_ERROR(("Software accelerator not initialized"));
415     decrypt_cb(FALSE, NULL, 0, context);
416     return NULL;
417   }
418
419   stack = silc_stack_alloc(2048, silc_crypto_stack());
420
421   e = silc_scalloc(stack, 1, sizeof(*e));
422   if (!e) {
423     silc_stack_free(stack);
424     decrypt_cb(FALSE, NULL, 0, context);
425     return NULL;
426   }
427
428   silc_stack_push(stack, NULL);
429
430   e->stack = stack;
431   e->type = SILC_SOFTACC_DECRYPT;
432   e->src = silc_smemdup(stack, src, src_len);
433   e->src_len = src_len;
434   e->key.private_key = privkey->key;
435   e->cb.decrypt_cb = decrypt_cb;
436   e->context = context;
437   silc_async_init(&e->op, silc_softacc_abort, NULL, e);
438
439   /* Run */
440   silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e,
441                        silc_softacc_completion, e);
442
443   return &e->op;
444 }
445
446 /* Accelerated signature */
447
448 SILC_PKCS_ALG_SIGN(silc_softacc_sign)
449 {
450   SilcSoftaccPrivateKey privkey = private_key;
451   SilcStack stack;
452   SilcSoftaccExec e;
453
454   SILC_LOG_DEBUG(("Sign"));
455
456   if (!sa) {
457     SILC_LOG_ERROR(("Software accelerator not initialized"));
458     sign_cb(FALSE, NULL, 0, context);
459     return NULL;
460   }
461
462   stack = silc_stack_alloc(2048, silc_crypto_stack());
463
464   e = silc_scalloc(stack, 1, sizeof(*e));
465   if (!e) {
466     silc_stack_free(stack);
467     sign_cb(FALSE, NULL, 0, context);
468     return NULL;
469   }
470
471   silc_stack_push(stack, NULL);
472
473   e->stack = stack;
474   e->type = SILC_SOFTACC_SIGN;
475   e->src = silc_smemdup(stack, src, src_len);
476   e->src_len = src_len;
477   e->compute_hash = compute_hash;
478   e->hash = hash;
479   e->key.private_key = privkey->key;
480   e->cb.sign_cb = sign_cb;
481   e->context = context;
482   silc_async_init(&e->op, silc_softacc_abort, NULL, e);
483
484   /* Run */
485   silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e,
486                        silc_softacc_completion, e);
487
488   return &e->op;
489 }
490
491 /* Accelerated verification */
492
493 SILC_PKCS_ALG_VERIFY(silc_softacc_verify)
494 {
495   SilcSoftaccPublicKey pubkey = public_key;
496   SilcStack stack;
497   SilcSoftaccExec e;
498
499   SILC_LOG_DEBUG(("Verify"));
500
501   if (!sa) {
502     SILC_LOG_ERROR(("Software accelerator not initialized"));
503     verify_cb(FALSE, context);
504     return NULL;
505   }
506
507   stack = silc_stack_alloc(2048, silc_crypto_stack());
508
509   e = silc_scalloc(stack, 1, sizeof(*e));
510   if (!e) {
511     silc_stack_free(stack);
512     verify_cb(FALSE, context);
513     return NULL;
514   }
515
516   silc_stack_push(stack, NULL);
517
518   e->stack = stack;
519   e->type = SILC_SOFTACC_VERIFY;
520   e->src = silc_smemdup(stack, signature, signature_len);
521   e->src_len = signature_len;
522   e->data = silc_smemdup(stack, data, data_len);
523   e->data_len = data_len;
524   e->hash = hash;
525   e->key.public_key = pubkey->key;
526   e->cb.verify_cb = verify_cb;
527   e->context = context;
528   silc_async_init(&e->op, silc_softacc_abort, NULL, e);
529
530   /* Run */
531   silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e,
532                        silc_softacc_completion, e);
533
534   return &e->op;
535 }