📄 cryptosoft.c
字号:
/* * An OCF module that uses the linux kernel cryptoapi, based on the * original cryptosoft for BSD by Angelos D. Keromytis (angelos@cis.upenn.edu) * but is mostly unrecognisable, * * Copyright (C) 2004 David McCullough <davidm@snapgear.com> * All rights reserved. * * LICENSE TERMS * * The free distribution and use of this software in both source and binary * form is allowed (with or without changes) provided that: * * 1. distributions of this source code include the above copyright * notice, this list of conditions and the following disclaimer; * * 2. distributions in binary form include the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other associated materials; * * 3. the copyright holder's name is not used to endorse products * built using this software without specific written permission. * * ALTERNATIVELY, provided that this notice is retained in full, this product * may be distributed under the terms of the GNU General Public License (GPL), * in which case the provisions of the GPL apply INSTEAD OF those given above. * * DISCLAIMER * * This software is provided 'as is' with no explicit or implied warranties * in respect of its properties, including, but not limited to, correctness * and/or fitness for purpose. * --------------------------------------------------------------------------- */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/list.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/wait.h>#include <linux/crypto.h>#include <asm/scatterlist.h>#include <cryptodev.h>#include <uio.h>#define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK)/* Software session entry */#define SW_TYPE_CIPHER 0#define SW_TYPE_HMAC 1#define SW_TYPE_AUTH2 2#define SW_TYPE_AUTH3 3#define SW_TYPE_COMP 4struct swcr_data { int sw_type; int sw_alg; struct crypto_tfm *sw_tfm; union { struct { char sw_key[HMAC_BLOCK_LEN]; int sw_klen; } hmac; } u; struct swcr_data *sw_next;};static int32_t swcr_id = -1;static struct swcr_data **swcr_sessions = NULL;static u_int32_t swcr_sesnum = 0;static int swcr_process(void *, struct cryptop *, int);static int swcr_newsession(void *, u_int32_t *, struct cryptoini *);static int swcr_freesession(void *, u_int64_t);static int debug = 0;MODULE_PARM(debug, "i");MODULE_PARM_DESC(debug, "Enable debug");/* * add data into a potentially chained/scattered buffer */static voidswcr_inject_data(int type, void *buf, unsigned char *result, int offset){ printk("%s,%d: %s()\n", __FILE__, __LINE__, __FUNCTION__);}/* * Generate a new software session. */static intswcr_newsession(void *arg, u_int32_t *sid, struct cryptoini *cri){ struct swcr_data **swd; u_int32_t i; int error; char *algo; int mode, sw_type; dprintk("%s()\n", __FUNCTION__); if (sid == NULL || cri == NULL) { dprintk("%s,%d - EINVAL\n", __FILE__, __LINE__); return EINVAL; } if (swcr_sessions) { for (i = 1; i < swcr_sesnum; i++) if (swcr_sessions[i] == NULL) break; } else i = 1; /* NB: to silence compiler warning */ if (swcr_sessions == NULL || i == swcr_sesnum) { if (swcr_sessions == NULL) { i = 1; /* We leave swcr_sessions[0] empty */ swcr_sesnum = CRYPTO_SW_SESSIONS; } else swcr_sesnum *= 2; swd = kmalloc(swcr_sesnum * sizeof(struct swcr_data *), GFP_ATOMIC); if (swd == NULL) { /* Reset session number */ if (swcr_sesnum == CRYPTO_SW_SESSIONS) swcr_sesnum = 0; else swcr_sesnum /= 2; dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__); return ENOBUFS; } memset(swd, 0, swcr_sesnum * sizeof(struct swcr_data *)); /* Copy existing sessions */ if (swcr_sessions) { memcpy(swd, swcr_sessions, (swcr_sesnum / 2) * sizeof(struct swcr_data *)); kfree(swcr_sessions); } swcr_sessions = swd; } swd = &swcr_sessions[i]; *sid = i; while (cri) { *swd = (struct swcr_data *) kmalloc(sizeof(struct swcr_data), GFP_ATOMIC); if (*swd == NULL) { swcr_freesession(NULL, i); dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); return ENOBUFS; } memset(*swd, 0, sizeof(struct swcr_data)); algo = NULL; mode = 0; sw_type = SW_TYPE_CIPHER; switch (cri->cri_alg) { case CRYPTO_DES_CBC: algo = "des"; mode = CRYPTO_TFM_MODE_CBC; break; case CRYPTO_3DES_CBC: algo = "des3_ede"; mode = CRYPTO_TFM_MODE_CBC; break; case CRYPTO_BLF_CBC: algo = "blowfish"; mode = CRYPTO_TFM_MODE_CBC; break; case CRYPTO_CAST_CBC: algo = "cast5"; mode = CRYPTO_TFM_MODE_CBC; break; case CRYPTO_SKIPJACK_CBC: algo = "skipjack"; mode = CRYPTO_TFM_MODE_CBC; break; case CRYPTO_RIJNDAEL128_CBC: algo = "aes"; mode = CRYPTO_TFM_MODE_CBC; break; case CRYPTO_NULL_CBC: algo = "cipher_null"; mode = 0; cri->cri_klen = 0; /* make it work with crypto API */ break; case CRYPTO_MD5_HMAC: algo = "md5"; sw_type = SW_TYPE_HMAC; break; case CRYPTO_SHA1_HMAC: algo = "sha1"; sw_type = SW_TYPE_HMAC; break; case CRYPTO_SHA2_HMAC: if (cri->cri_klen == 256) algo = "sha256"; else if (cri->cri_klen == 384) algo = "sha384"; else if (cri->cri_klen == 512) algo = "sha512"; sw_type = SW_TYPE_HMAC; break; case CRYPTO_NULL_HMAC: algo = "digest_null"; sw_type = SW_TYPE_HMAC; break; case CRYPTO_RIPEMD160_HMAC: algo = "ripemd160"; sw_type = SW_TYPE_HMAC; break; case CRYPTO_MD5_KPDK: algo = "??"; sw_type = SW_TYPE_AUTH2; break; case CRYPTO_SHA1_KPDK: algo = "??"; sw_type = SW_TYPE_AUTH2; break; case CRYPTO_MD5: algo = "md5"; sw_type = SW_TYPE_AUTH3; break; case CRYPTO_SHA1: algo = "sha1"; sw_type = SW_TYPE_AUTH3; break; case CRYPTO_DEFLATE_COMP: algo = "deflate"; sw_type = SW_TYPE_COMP; break; default: break; } if (!algo || !*algo) { printk("cryptosoft: Unknown algo 0x%x\n", cri->cri_alg); swcr_freesession(NULL, i); return EINVAL; } dprintk("%s crypto_alloc_tfm(%s, 0x%x)\n", __FUNCTION__, algo, mode); (*swd)->sw_tfm = crypto_alloc_tfm(algo, mode); if (!(*swd)->sw_tfm) { printk("cryptosoft: crypto_alloc_tfm failed(%s,0x%x)\n",algo,mode); swcr_freesession(NULL, i); return EINVAL; } if (sw_type == SW_TYPE_CIPHER) { if (debug) { dprintk("%s key:", __FUNCTION__); for (i = 0; i < cri->cri_klen / 8; i++) dprintk("%s0x%x", (i % 8) ? " " : "\n ",cri->cri_key[i]); dprintk("\n"); } error = crypto_cipher_setkey((*swd)->sw_tfm, cri->cri_key, cri->cri_klen / 8); if (error) { printk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n", error, (*swd)->sw_tfm->crt_flags); swcr_freesession(NULL, i); return error; } } else if (sw_type == SW_TYPE_HMAC) { (*swd)->u.hmac.sw_klen = cri->cri_klen / 8; memcpy((*swd)->u.hmac.sw_key, cri->cri_key, (*swd)->u.hmac.sw_klen); } else { printk("cryptosoft: Unhandled sw_type %d\n", sw_type); swcr_freesession(NULL, i); return EINVAL; } (*swd)->sw_alg = cri->cri_alg; (*swd)->sw_type = sw_type; cri = cri->cri_next; swd = &((*swd)->sw_next); } return 0;}/* * Free a session. */static intswcr_freesession(void *arg, u_int64_t tid){ struct swcr_data *swd; u_int32_t sid = CRYPTO_SESID2LID(tid); dprintk("%s()\n", __FUNCTION__); if (sid > swcr_sesnum || swcr_sessions == NULL || swcr_sessions[sid] == NULL) { dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); return(EINVAL); } /* Silently accept and return */ if (sid == 0) return(0); while ((swd = swcr_sessions[sid]) != NULL) { swcr_sessions[sid] = swd->sw_next; if (swd->sw_tfm) crypto_free_tfm(swd->sw_tfm); kfree(swd); } return 0;}/* * Process a software request. */static intswcr_process(void *arg, struct cryptop *crp, int hint){ struct cryptodesc *crd; struct swcr_data *sw; u_int32_t lid; int type; struct scatterlist sg[16]; struct uio *uiop; int sg_num, sg_len; dprintk("%s()\n", __FUNCTION__); /* Sanity check */ if (crp == NULL) { dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); return EINVAL; } crp->crp_etype = 0; if (crp->crp_desc == NULL || crp->crp_buf == NULL) { dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); crp->crp_etype = EINVAL; goto done; } lid = crp->crp_sid & 0xffffffff; if (lid >= swcr_sesnum || lid == 0 || swcr_sessions == NULL || swcr_sessions[lid] == NULL) { crp->crp_etype = ENOENT; dprintk("%s,%d: ENOENT\n", __FILE__, __LINE__); goto done; } if (crp->crp_flags & CRYPTO_F_IMBUF) { type = CRYPTO_BUF_MBUF; panic("CRYPTO_F_IMBUF"); goto done; } else if (crp->crp_flags & CRYPTO_F_IOV) { type = CRYPTO_BUF_IOV; uiop = (struct uio *) crp->crp_buf; sg_len = 0; for (sg_num = 0; sg_num < uiop->uio_iovcnt && sg_num < 16; sg_num++) { sg[sg_num].address = 0; sg[sg_num].page = virt_to_page(uiop->uio_iov[sg_num].iov_base); sg[sg_num].offset = offset_in_page(uiop->uio_iov[sg_num].iov_base); sg[sg_num].length = uiop->uio_iov[sg_num].iov_len; sg_len += sg[sg_num].length; } } else { type = CRYPTO_BUF_CONTIG; sg[0].address = 0; sg[0].page = virt_to_page(crp->crp_buf); sg[0].offset = offset_in_page(crp->crp_buf); sg_len = sg[0].length = crp->crp_ilen; sg_num = 1; } /* Go through crypto descriptors, processing as we go */ for (crd = crp->crp_desc; crd; crd = crd->crd_next) { /* * Find the crypto context. * * XXX Note that the logic here prevents us from having * XXX the same algorithm multiple times in a session * XXX (or rather, we can but it won't give us the right * XXX results). To do that, we'd need some way of differentiating * XXX between the various instances of an algorithm (so we can * XXX locate the correct crypto context). */ for (sw = swcr_sessions[lid]; sw && sw->sw_alg != crd->crd_alg; sw = sw->sw_next) ; /* No such context ? */ if (sw == NULL) { crp->crp_etype = EINVAL; dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); goto done; } if (sg_len < crypto_tfm_alg_blocksize(sw->sw_tfm)) { crp->crp_etype = EINVAL; dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); goto done; } switch (sw->sw_type) { case SW_TYPE_CIPHER: if (crd->crd_flags & CRD_F_IV_EXPLICIT) crypto_cipher_set_iv(sw->sw_tfm, crd->crd_iv, crypto_tfm_alg_ivsize(sw->sw_tfm)); if (crd->crd_flags & CRD_F_ENCRYPT) crypto_cipher_encrypt(sw->sw_tfm, sg, sg, sg_len); else crypto_cipher_decrypt(sw->sw_tfm, sg, sg, sg_len); break; case SW_TYPE_HMAC: /* * BSD and linux seem to differ on digests a little. Linux * has a block size of 64, while BSD ranges from 8 up. I * don't know what the difference is, but we have to check it * here before calling into the linux crypto or we will be very * unhappy with the results */ if (sg_len - crd->crd_inject < crypto_tfm_alg_digestsize(sw->sw_tfm)) { dprintk("cryptosoft: EINVAL len=%d, digestsize=%d\n", sg_len, crypto_tfm_alg_digestsize(sw->sw_tfm)); crp->crp_etype = EINVAL; goto done; } if (type == CRYPTO_BUF_CONTIG) { crypto_hmac(sw->sw_tfm, sw->u.hmac.sw_key, &sw->u.hmac.sw_klen, sg, sg_num, crp->crp_buf + crd->crd_inject); } else { char result[AALG_MAX_RESULT_LEN]; crypto_hmac(sw->sw_tfm, sw->u.hmac.sw_key, &sw->u.hmac.sw_klen, sg, sg_num, result); if (type == CRYPTO_BUF_IOV) { cuio_copyback((struct uio *) crp->crp_buf, crd->crd_inject, crypto_tfm_alg_digestsize(sw->sw_tfm), result); } else swcr_inject_data(type,crp->crp_buf,result,crd->crd_inject); } break; case SW_TYPE_COMP:#if 0 data = allocate contiguous buffer (crp->crp_buf, crd->crd_len) if (crd->crd_flags & CRD_F_COMP) ret = crypto_comp_compress(sw->sw_tfm, data, len, result, &dlen); else ret = crypto_comp_decompress(sw->sw_tfm, data, len, result, &dlen);#endif break; default: /* Unknown/unsupported algorithm */ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); crp->crp_etype = EINVAL; goto done; } }done: crypto_done(crp); return 0;}static intcryptosoft_init(void){ dprintk("%s(%p)\n", __FUNCTION__, cryptosoft_init); swcr_id = crypto_get_driverid(CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC); if (swcr_id < 0) panic("Software crypto device cannot initialize!"); crypto_register(swcr_id, CRYPTO_DES_CBC, 0, 0, swcr_newsession, swcr_freesession, swcr_process, NULL);#define REGISTER(alg) \ crypto_register(swcr_id, alg, 0,0,NULL,NULL,NULL,NULL) REGISTER(CRYPTO_3DES_CBC); REGISTER(CRYPTO_BLF_CBC); REGISTER(CRYPTO_CAST_CBC); REGISTER(CRYPTO_SKIPJACK_CBC); REGISTER(CRYPTO_NULL_CBC); REGISTER(CRYPTO_MD5_HMAC); REGISTER(CRYPTO_SHA1_HMAC); REGISTER(CRYPTO_SHA2_HMAC); REGISTER(CRYPTO_RIPEMD160_HMAC); REGISTER(CRYPTO_NULL_HMAC); REGISTER(CRYPTO_MD5_KPDK); REGISTER(CRYPTO_SHA1_KPDK); REGISTER(CRYPTO_MD5); REGISTER(CRYPTO_SHA1); REGISTER(CRYPTO_RIJNDAEL128_CBC); REGISTER(CRYPTO_DEFLATE_COMP);#undef REGISTER return(0);}static voidcryptosoft_exit(void){ dprintk("%s()\n", __FUNCTION__); crypto_unregister_all(swcr_id); swcr_id = -1;}module_init(cryptosoft_init);module_exit(cryptosoft_exit);MODULE_LICENSE("Dual BSD/GPL");MODULE_AUTHOR("davidm@snapgear.com");MODULE_DESCRIPTION("Cryptosoft (OCF module for kernel crypto)");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -