Added SILC Thread Queue API
[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     sa = NULL;
169     return FALSE;
170   }
171
172   return TRUE;
173 }
174
175 /* Uninitialize */
176
177 SilcBool silc_softacc_uninit(void)
178 {
179   if (!sa)
180     return FALSE;
181
182   SILC_LOG_DEBUG(("Uninitialize software accelerator"));
183
184   silc_thread_pool_free(sa->tp, TRUE);
185   silc_free(sa);
186   sa = NULL;
187
188   return TRUE;
189 }
190
191 /****************************** PKCS ALG API ********************************/
192
193 /* Abort operation */
194
195 void silc_softacc_abort(SilcAsyncOperation op, void *context)
196 {
197   SilcSoftaccExec e = context;
198   e->aborted = TRUE;
199 }
200
201 /* Accelerator completion, executed in main thread. */
202
203 SILC_TASK_CALLBACK(silc_softacc_completion)
204 {
205   SilcSoftaccExec e = context;
206   SilcStack stack = e->stack;
207
208   /* At the latest, abort is catched here in the main thread.  Don't
209      deliver callback if we were aborted */
210   if (e->aborted)
211     goto out;
212
213   SILC_LOG_DEBUG(("Call completion, result=%s", e->result ? "Ok" : "failed"));
214
215   /* Call completion callback */
216   switch (e->type) {
217   case SILC_SOFTACC_ENCRYPT:
218     e->cb.encrypt_cb(e->result, e->result_data, e->result_len, e->context);
219     break;
220
221   case SILC_SOFTACC_DECRYPT:
222     e->cb.decrypt_cb(e->result, e->result_data, e->result_len, e->context);
223     break;
224
225   case SILC_SOFTACC_SIGN:
226     e->cb.sign_cb(e->result, e->result_data, e->result_len, e->context);
227     break;
228
229   case SILC_SOFTACC_VERIFY:
230     e->cb.verify_cb(e->result, e->context);
231     break;
232   }
233
234  out:
235   silc_sfree(stack, e->src);
236   silc_sfree(stack, e->data);
237   silc_sfree(stack, e->result_data);
238   silc_sfree(stack, e);
239   silc_stack_free(stack);
240 }
241
242 /* Callback for encrypt, decrypt and signature */
243
244 void silc_softacc_data_cb(SilcBool success, const unsigned char *data,
245                           SilcUInt32 data_len, void *context)
246 {
247   SilcSoftaccExec e = context;
248   SilcStack stack = e->stack;
249
250   /* Pop e->src */
251   silc_stack_pop(stack);
252
253   if (success)
254     e->result_data = silc_smemdup(stack, data, data_len);
255   e->result_len = data_len;
256   e->result = success;
257 }
258
259 /* Verification callback */
260
261 void silc_softacc_verify_cb(SilcBool success, void *context)
262 {
263   SilcSoftaccExec e = context;
264   SilcStack stack = e->stack;
265
266   /* Pop e->src and e->data from memory */
267   silc_stack_pop(stack);
268
269   e->result = success;
270 }
271
272 /* Accelerator thread */
273
274 void silc_softacc_thread(SilcSchedule schedule, void *context)
275 {
276   SilcSoftaccExec e = context;
277
278   if (e->aborted)
279     return;
280
281   SILC_LOG_DEBUG(("Execute type %d", e->type));
282
283   /* Call the operation */
284   switch (e->type) {
285   case SILC_SOFTACC_ENCRYPT:
286     silc_pkcs_encrypt(e->key.public_key, e->src, e->src_len, e->rng,
287                       silc_softacc_data_cb, e);
288     break;
289
290   case SILC_SOFTACC_DECRYPT:
291     silc_pkcs_decrypt(e->key.private_key, e->src, e->src_len,
292                       silc_softacc_data_cb, e);
293     break;
294
295   case SILC_SOFTACC_SIGN:
296     silc_pkcs_sign(e->key.private_key, e->src, e->src_len, e->compute_hash,
297                    e->hash, e->rng, silc_softacc_data_cb, e);
298     break;
299
300   case SILC_SOFTACC_VERIFY:
301     silc_pkcs_verify(e->key.public_key, e->src, e->src_len, e->data,
302                      e->data_len, e->hash, silc_softacc_verify_cb, e);
303     break;
304   }
305 }
306
307 /* Accelerate public key */
308
309 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_softacc_acc_public_key)
310 {
311   SilcSoftaccPublicKey pubkey;
312
313   if (!sa) {
314     SILC_LOG_ERROR(("Software accelerator not initialized"));
315     return FALSE;
316   }
317
318   pubkey = silc_calloc(1, sizeof(*pubkey));
319   if (!pubkey)
320     return FALSE;
321   pubkey->key = key;
322
323   *ret_public_key = pubkey;
324
325   return TRUE;
326 }
327
328 /* Accelerate private key */
329
330 SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_softacc_acc_private_key)
331 {
332   SilcSoftaccPrivateKey privkey;
333
334   if (!sa) {
335     SILC_LOG_ERROR(("Software accelerator not initialized"));
336     return FALSE;
337   }
338
339   privkey = silc_calloc(1, sizeof(*privkey));
340   if (!privkey)
341     return FALSE;
342   privkey->key = key;
343
344   *ret_private_key = privkey;
345
346   return TRUE;
347 }
348
349 /* Free public key */
350
351 SILC_PKCS_ALG_PUBLIC_KEY_FREE(silc_softacc_free_public_key)
352 {
353   silc_free(public_key);
354 }
355
356 /* Free private key */
357
358 SILC_PKCS_ALG_PRIVATE_KEY_FREE(silc_softacc_free_private_key)
359 {
360   silc_free(private_key);
361 }
362
363 /* Accelerated encrypt */
364
365 SILC_PKCS_ALG_ENCRYPT(silc_softacc_encrypt)
366 {
367   SilcSoftaccPublicKey pubkey = public_key;
368   SilcStack stack;
369   SilcSoftaccExec e;
370
371   SILC_LOG_DEBUG(("Encrypt"));
372
373   if (!sa) {
374     SILC_LOG_ERROR(("Software accelerator not initialized"));
375     encrypt_cb(FALSE, NULL, 0, context);
376     return NULL;
377   }
378
379   stack = silc_stack_alloc(2048, silc_crypto_stack());
380
381   e = silc_scalloc(stack, 1, sizeof(*e));
382   if (!e) {
383     silc_stack_free(stack);
384     encrypt_cb(FALSE, NULL, 0, context);
385     return NULL;
386   }
387
388   silc_stack_push(stack, NULL);
389
390   e->stack = stack;
391   e->type = SILC_SOFTACC_ENCRYPT;
392   e->src = silc_smemdup(stack, src, src_len);
393   e->src_len = src_len;
394   e->rng = rng;
395   e->key.public_key = pubkey->key;
396   e->cb.encrypt_cb = encrypt_cb;
397   e->context = context;
398   silc_async_init(&e->op, silc_softacc_abort, NULL, e);
399
400   /* Run */
401   silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e,
402                        silc_softacc_completion, e);
403
404   return &e->op;
405 }
406
407 /* Acceleted decrypt */
408
409 SILC_PKCS_ALG_DECRYPT(silc_softacc_decrypt)
410 {
411   SilcSoftaccPrivateKey privkey = private_key;
412   SilcStack stack;
413   SilcSoftaccExec e;
414
415   SILC_LOG_DEBUG(("Decrypt"));
416
417   if (!sa) {
418     SILC_LOG_ERROR(("Software accelerator not initialized"));
419     decrypt_cb(FALSE, NULL, 0, context);
420     return NULL;
421   }
422
423   stack = silc_stack_alloc(2048, silc_crypto_stack());
424
425   e = silc_scalloc(stack, 1, sizeof(*e));
426   if (!e) {
427     silc_stack_free(stack);
428     decrypt_cb(FALSE, NULL, 0, context);
429     return NULL;
430   }
431
432   silc_stack_push(stack, NULL);
433
434   e->stack = stack;
435   e->type = SILC_SOFTACC_DECRYPT;
436   e->src = silc_smemdup(stack, src, src_len);
437   e->src_len = src_len;
438   e->key.private_key = privkey->key;
439   e->cb.decrypt_cb = decrypt_cb;
440   e->context = context;
441   silc_async_init(&e->op, silc_softacc_abort, NULL, e);
442
443   /* Run */
444   silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e,
445                        silc_softacc_completion, e);
446
447   return &e->op;
448 }
449
450 /* Accelerated signature */
451
452 SILC_PKCS_ALG_SIGN(silc_softacc_sign)
453 {
454   SilcSoftaccPrivateKey privkey = private_key;
455   SilcStack stack;
456   SilcSoftaccExec e;
457
458   SILC_LOG_DEBUG(("Sign"));
459
460   if (!sa) {
461     SILC_LOG_ERROR(("Software accelerator not initialized"));
462     sign_cb(FALSE, NULL, 0, context);
463     return NULL;
464   }
465
466   stack = silc_stack_alloc(2048, silc_crypto_stack());
467
468   e = silc_scalloc(stack, 1, sizeof(*e));
469   if (!e) {
470     silc_stack_free(stack);
471     sign_cb(FALSE, NULL, 0, context);
472     return NULL;
473   }
474
475   silc_stack_push(stack, NULL);
476
477   e->stack = stack;
478   e->type = SILC_SOFTACC_SIGN;
479   e->rng = rng;
480   e->src = silc_smemdup(stack, src, src_len);
481   e->src_len = src_len;
482   e->compute_hash = compute_hash;
483   e->hash = hash;
484   e->key.private_key = privkey->key;
485   e->cb.sign_cb = sign_cb;
486   e->context = context;
487   silc_async_init(&e->op, silc_softacc_abort, NULL, e);
488
489   /* Run */
490   silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e,
491                        silc_softacc_completion, e);
492
493   return &e->op;
494 }
495
496 /* Accelerated verification */
497
498 SILC_PKCS_ALG_VERIFY(silc_softacc_verify)
499 {
500   SilcSoftaccPublicKey pubkey = public_key;
501   SilcStack stack;
502   SilcSoftaccExec e;
503
504   SILC_LOG_DEBUG(("Verify"));
505
506   if (!sa) {
507     SILC_LOG_ERROR(("Software accelerator not initialized"));
508     verify_cb(FALSE, context);
509     return NULL;
510   }
511
512   stack = silc_stack_alloc(2048, silc_crypto_stack());
513
514   e = silc_scalloc(stack, 1, sizeof(*e));
515   if (!e) {
516     silc_stack_free(stack);
517     verify_cb(FALSE, context);
518     return NULL;
519   }
520
521   silc_stack_push(stack, NULL);
522
523   e->stack = stack;
524   e->type = SILC_SOFTACC_VERIFY;
525   e->src = silc_smemdup(stack, signature, signature_len);
526   e->src_len = signature_len;
527   e->data = silc_smemdup(stack, data, data_len);
528   e->data_len = data_len;
529   e->hash = hash;
530   e->key.public_key = pubkey->key;
531   e->cb.verify_cb = verify_cb;
532   e->context = context;
533   silc_async_init(&e->op, silc_softacc_abort, NULL, e);
534
535   /* Run */
536   silc_thread_pool_run(sa->tp, TRUE, sa->schedule, silc_softacc_thread, e,
537                        silc_softacc_completion, e);
538
539   return &e->op;
540 }