/* * sha1_provider.c * * Copyright (c) 2004 Evgeniy Polyakov * * * 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; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include "acrypto.h" #include "crypto_stat.h" static void prov_data_ready(struct crypto_device *dev); static struct crypto_capability prov_caps[] = { {3, 3, 3, 100} }; static int prov_cap_number = sizeof(prov_caps)/sizeof(prov_caps[0]); static struct completion thread_exited; static DECLARE_WAIT_QUEUE_HEAD(sha1_wait_queue); static int need_exit; static struct crypto_session *sp; static struct crypto_tfm *tfm; static struct crypto_device pdev = { .name = "sha1_provider", .data_ready = prov_data_ready, .cap = &prov_caps[0], }; static void prov_data_ready(struct crypto_device *dev) { struct crypto_session *s, *n; if (!spin_trylock_bh(&dev->session_lock)) return; list_for_each_entry_safe(s, n, &dev->session_list, dev_queue_entry) { if (!session_completed(s)) { sp = s; wake_up(&sha1_wait_queue); break; } } spin_unlock_bh(&dev->session_lock); } static int sha1_thread(void *data) { struct crypto_device *dev = (struct crypto_device *)data; u8 *key, *out; unsigned int keylen; daemonize("%s", dev->name); allow_signal(SIGTERM); while (!need_exit) { interruptible_sleep_on(&sha1_wait_queue); if (need_exit) break; if (!sp) continue; start_process_session(sp); key = ((u8 *)page_address(sp->data.sg_key.page)) + sp->data.sg_key.offset; keylen = sp->data.sg_key.length; out = ((u8 *)page_address(sp->data.sg_dst.page)) + sp->data.sg_dst.offset; crypto_hmac(tfm, key, &keylen, &sp->data.sg_src, sp->data.sg_src_num, out); sp->data.sg_dst.length = 20; dprintk("Completing session %llu [%llu] in %s.\n", sp->ci.id, sp->ci.dev_id, pdev.name); crypto_stat_complete_inc(sp); complete_session(sp); stop_process_session(sp); prov_data_ready(dev); sp = NULL; } complete_and_exit(&thread_exited, 0); } int prov_init(void) { int err, pid; tfm = crypto_alloc_tfm("sha1", 0); if (!tfm) { printk(KERN_ERR "Failed to allocate SHA1 tfm.\n"); return -EINVAL; } init_completion(&thread_exited); pid = kernel_thread(sha1_thread, &pdev, CLONE_FS | CLONE_FILES); if (IS_ERR((void *)pid)) { err = -EINVAL; dprintk(KERN_ERR "Failed to create kernel load balancing thread.\n"); goto err_out_free_tfm; } pdev.cap_number = prov_cap_number; err = crypto_device_add(&pdev); if (err) goto err_out_remove_thread; dprintk(KERN_INFO "Test crypto provider module %s is loaded.\n", pdev.name); return 0; err_out_remove_thread: need_exit = 1; wake_up(&sha1_wait_queue); wait_for_completion(&thread_exited); err_out_free_tfm: crypto_free_tfm(tfm); return err; } void prov_fini(void) { need_exit = 1; wake_up(&sha1_wait_queue); wait_for_completion(&thread_exited); crypto_device_remove(&pdev); crypto_free_tfm(tfm); dprintk(KERN_INFO "Test crypto provider module %s is unloaded.\n", pdev.name); } module_init(prov_init); module_exit(prov_fini); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Evgeniy Polyakov "); MODULE_DESCRIPTION("Test crypto module provider.");