891 lines · 21.5 KB · C
RawDownload
/* * CCM: Counter with CBC-MAC * * (C) Copyright IBM Corp. 2007 - Joy Latten <latten@us.ibm.com> * * 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. * */ #include <crypto/internal/aead.h>#include <crypto/internal/skcipher.h>#include <crypto/scatterwalk.h>#include <linux/err.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h> #include "internal.h" struct ccm_instance_ctx {	struct crypto_skcipher_spawn ctr;	struct crypto_spawn cipher;}; struct crypto_ccm_ctx {	struct crypto_cipher *cipher;	struct crypto_ablkcipher *ctr;}; struct crypto_rfc4309_ctx {	struct crypto_aead *child;	u8 nonce[3];}; struct crypto_ccm_req_priv_ctx {	u8 odata[16];	u8 idata[16];	u8 auth_tag[16];	u32 ilen;	u32 flags;	struct scatterlist src[2];	struct scatterlist dst[2];	struct ablkcipher_request abreq;}; static inline struct crypto_ccm_req_priv_ctx *crypto_ccm_reqctx(	struct aead_request *req){	unsigned long align = crypto_aead_alignmask(crypto_aead_reqtfm(req)); 	return (void *)PTR_ALIGN((u8 *)aead_request_ctx(req), align + 1);} static int set_msg_len(u8 *block, unsigned int msglen, int csize){	__be32 data; 	memset(block, 0, csize);	block += csize; 	if (csize >= 4)		csize = 4;	else if (msglen > (1 << (8 * csize)))		return -EOVERFLOW; 	data = cpu_to_be32(msglen);	memcpy(block - csize, (u8 *)&data + 4 - csize, csize); 	return 0;} static int crypto_ccm_setkey(struct crypto_aead *aead, const u8 *key,			     unsigned int keylen){	struct crypto_ccm_ctx *ctx = crypto_aead_ctx(aead);	struct crypto_ablkcipher *ctr = ctx->ctr;	struct crypto_cipher *tfm = ctx->cipher;	int err = 0; 	crypto_ablkcipher_clear_flags(ctr, CRYPTO_TFM_REQ_MASK);	crypto_ablkcipher_set_flags(ctr, crypto_aead_get_flags(aead) &				    CRYPTO_TFM_REQ_MASK);	err = crypto_ablkcipher_setkey(ctr, key, keylen);	crypto_aead_set_flags(aead, crypto_ablkcipher_get_flags(ctr) &			      CRYPTO_TFM_RES_MASK);	if (err)		goto out; 	crypto_cipher_clear_flags(tfm, CRYPTO_TFM_REQ_MASK);	crypto_cipher_set_flags(tfm, crypto_aead_get_flags(aead) &				    CRYPTO_TFM_REQ_MASK);	err = crypto_cipher_setkey(tfm, key, keylen);	crypto_aead_set_flags(aead, crypto_cipher_get_flags(tfm) &			      CRYPTO_TFM_RES_MASK); out:	return err;} static int crypto_ccm_setauthsize(struct crypto_aead *tfm,				  unsigned int authsize){	switch (authsize) {	case 4:	case 6:	case 8:	case 10:	case 12:	case 14:	case 16:		break;	default:		return -EINVAL;	} 	return 0;} static int format_input(u8 *info, struct aead_request *req,			unsigned int cryptlen){	struct crypto_aead *aead = crypto_aead_reqtfm(req);	unsigned int lp = req->iv[0];	unsigned int l = lp + 1;	unsigned int m; 	m = crypto_aead_authsize(aead); 	memcpy(info, req->iv, 16); 	/* format control info per RFC 3610 and	 * NIST Special Publication 800-38C	 */	*info |= (8 * ((m - 2) / 2));	if (req->assoclen)		*info |= 64; 	return set_msg_len(info + 16 - l, cryptlen, l);} static int format_adata(u8 *adata, unsigned int a){	int len = 0; 	/* add control info for associated data	 * RFC 3610 and NIST Special Publication 800-38C	 */	if (a < 65280) {		*(__be16 *)adata = cpu_to_be16(a);		len = 2;	} else  {		*(__be16 *)adata = cpu_to_be16(0xfffe);		*(__be32 *)&adata[2] = cpu_to_be32(a);		len = 6;	} 	return len;} static void compute_mac(struct crypto_cipher *tfm, u8 *data, int n,		       struct crypto_ccm_req_priv_ctx *pctx){	unsigned int bs = 16;	u8 *odata = pctx->odata;	u8 *idata = pctx->idata;	int datalen, getlen; 	datalen = n; 	/* first time in here, block may be partially filled. */	getlen = bs - pctx->ilen;	if (datalen >= getlen) {		memcpy(idata + pctx->ilen, data, getlen);		crypto_xor(odata, idata, bs);		crypto_cipher_encrypt_one(tfm, odata, odata);		datalen -= getlen;		data += getlen;		pctx->ilen = 0;	} 	/* now encrypt rest of data */	while (datalen >= bs) {		crypto_xor(odata, data, bs);		crypto_cipher_encrypt_one(tfm, odata, odata); 		datalen -= bs;		data += bs;	} 	/* check and see if there's leftover data that wasn't	 * enough to fill a block.	 */	if (datalen) {		memcpy(idata + pctx->ilen, data, datalen);		pctx->ilen += datalen;	}} static void get_data_to_compute(struct crypto_cipher *tfm,			       struct crypto_ccm_req_priv_ctx *pctx,			       struct scatterlist *sg, unsigned int len){	struct scatter_walk walk;	u8 *data_src;	int n; 	scatterwalk_start(&walk, sg); 	while (len) {		n = scatterwalk_clamp(&walk, len);		if (!n) {			scatterwalk_start(&walk, sg_next(walk.sg));			n = scatterwalk_clamp(&walk, len);		}		data_src = scatterwalk_map(&walk); 		compute_mac(tfm, data_src, n, pctx);		len -= n; 		scatterwalk_unmap(data_src);		scatterwalk_advance(&walk, n);		scatterwalk_done(&walk, 0, len);		if (len)			crypto_yield(pctx->flags);	} 	/* any leftover needs padding and then encrypted */	if (pctx->ilen) {		int padlen;		u8 *odata = pctx->odata;		u8 *idata = pctx->idata; 		padlen = 16 - pctx->ilen;		memset(idata + pctx->ilen, 0, padlen);		crypto_xor(odata, idata, 16);		crypto_cipher_encrypt_one(tfm, odata, odata);		pctx->ilen = 0;	}} static int crypto_ccm_auth(struct aead_request *req, struct scatterlist *plain,			   unsigned int cryptlen){	struct crypto_aead *aead = crypto_aead_reqtfm(req);	struct crypto_ccm_ctx *ctx = crypto_aead_ctx(aead);	struct crypto_ccm_req_priv_ctx *pctx = crypto_ccm_reqctx(req);	struct crypto_cipher *cipher = ctx->cipher;	unsigned int assoclen = req->assoclen;	u8 *odata = pctx->odata;	u8 *idata = pctx->idata;	int err; 	/* format control data for input */	err = format_input(odata, req, cryptlen);	if (err)		goto out; 	/* encrypt first block to use as start in computing mac  */	crypto_cipher_encrypt_one(cipher, odata, odata); 	/* format associated data and compute into mac */	if (assoclen) {		pctx->ilen = format_adata(idata, assoclen);		get_data_to_compute(cipher, pctx, req->assoc, req->assoclen);	} else {		pctx->ilen = 0;	} 	/* compute plaintext into mac */	get_data_to_compute(cipher, pctx, plain, cryptlen); out:	return err;} static void crypto_ccm_encrypt_done(struct crypto_async_request *areq, int err){	struct aead_request *req = areq->data;	struct crypto_aead *aead = crypto_aead_reqtfm(req);	struct crypto_ccm_req_priv_ctx *pctx = crypto_ccm_reqctx(req);	u8 *odata = pctx->odata; 	if (!err)		scatterwalk_map_and_copy(odata, req->dst, req->cryptlen,					 crypto_aead_authsize(aead), 1);	aead_request_complete(req, err);} static inline int crypto_ccm_check_iv(const u8 *iv){	/* 2 <= L <= 8, so 1 <= L' <= 7. */	if (1 > iv[0] || iv[0] > 7)		return -EINVAL; 	return 0;} static int crypto_ccm_encrypt(struct aead_request *req){	struct crypto_aead *aead = crypto_aead_reqtfm(req);	struct crypto_ccm_ctx *ctx = crypto_aead_ctx(aead);	struct crypto_ccm_req_priv_ctx *pctx = crypto_ccm_reqctx(req);	struct ablkcipher_request *abreq = &pctx->abreq;	struct scatterlist *dst;	unsigned int cryptlen = req->cryptlen;	u8 *odata = pctx->odata;	u8 *iv = req->iv;	int err; 	err = crypto_ccm_check_iv(iv);	if (err)		return err; 	pctx->flags = aead_request_flags(req); 	err = crypto_ccm_auth(req, req->src, cryptlen);	if (err)		return err; 	 /* Note: rfc 3610 and NIST 800-38C require counter of	 * zero to encrypt auth tag.	 */	memset(iv + 15 - iv[0], 0, iv[0] + 1); 	sg_init_table(pctx->src, 2);	sg_set_buf(pctx->src, odata, 16);	scatterwalk_sg_chain(pctx->src, 2, req->src); 	dst = pctx->src;	if (req->src != req->dst) {		sg_init_table(pctx->dst, 2);		sg_set_buf(pctx->dst, odata, 16);		scatterwalk_sg_chain(pctx->dst, 2, req->dst);		dst = pctx->dst;	} 	ablkcipher_request_set_tfm(abreq, ctx->ctr);	ablkcipher_request_set_callback(abreq, pctx->flags,					crypto_ccm_encrypt_done, req);	ablkcipher_request_set_crypt(abreq, pctx->src, dst, cryptlen + 16, iv);	err = crypto_ablkcipher_encrypt(abreq);	if (err)		return err; 	/* copy authtag to end of dst */	scatterwalk_map_and_copy(odata, req->dst, cryptlen,				 crypto_aead_authsize(aead), 1);	return err;} static void crypto_ccm_decrypt_done(struct crypto_async_request *areq,				   int err){	struct aead_request *req = areq->data;	struct crypto_ccm_req_priv_ctx *pctx = crypto_ccm_reqctx(req);	struct crypto_aead *aead = crypto_aead_reqtfm(req);	unsigned int authsize = crypto_aead_authsize(aead);	unsigned int cryptlen = req->cryptlen - authsize; 	if (!err) {		err = crypto_ccm_auth(req, req->dst, cryptlen);		if (!err && memcmp(pctx->auth_tag, pctx->odata, authsize))			err = -EBADMSG;	}	aead_request_complete(req, err);} static int crypto_ccm_decrypt(struct aead_request *req){	struct crypto_aead *aead = crypto_aead_reqtfm(req);	struct crypto_ccm_ctx *ctx = crypto_aead_ctx(aead);	struct crypto_ccm_req_priv_ctx *pctx = crypto_ccm_reqctx(req);	struct ablkcipher_request *abreq = &pctx->abreq;	struct scatterlist *dst;	unsigned int authsize = crypto_aead_authsize(aead);	unsigned int cryptlen = req->cryptlen;	u8 *authtag = pctx->auth_tag;	u8 *odata = pctx->odata;	u8 *iv = req->iv;	int err; 	if (cryptlen < authsize)		return -EINVAL;	cryptlen -= authsize; 	err = crypto_ccm_check_iv(iv);	if (err)		return err; 	pctx->flags = aead_request_flags(req); 	scatterwalk_map_and_copy(authtag, req->src, cryptlen, authsize, 0); 	memset(iv + 15 - iv[0], 0, iv[0] + 1); 	sg_init_table(pctx->src, 2);	sg_set_buf(pctx->src, authtag, 16);	scatterwalk_sg_chain(pctx->src, 2, req->src); 	dst = pctx->src;	if (req->src != req->dst) {		sg_init_table(pctx->dst, 2);		sg_set_buf(pctx->dst, authtag, 16);		scatterwalk_sg_chain(pctx->dst, 2, req->dst);		dst = pctx->dst;	} 	ablkcipher_request_set_tfm(abreq, ctx->ctr);	ablkcipher_request_set_callback(abreq, pctx->flags,					crypto_ccm_decrypt_done, req);	ablkcipher_request_set_crypt(abreq, pctx->src, dst, cryptlen + 16, iv);	err = crypto_ablkcipher_decrypt(abreq);	if (err)		return err; 	err = crypto_ccm_auth(req, req->dst, cryptlen);	if (err)		return err; 	/* verify */	if (memcmp(authtag, odata, authsize))		return -EBADMSG; 	return err;} static int crypto_ccm_init_tfm(struct crypto_tfm *tfm){	struct crypto_instance *inst = (void *)tfm->__crt_alg;	struct ccm_instance_ctx *ictx = crypto_instance_ctx(inst);	struct crypto_ccm_ctx *ctx = crypto_tfm_ctx(tfm);	struct crypto_cipher *cipher;	struct crypto_ablkcipher *ctr;	unsigned long align;	int err; 	cipher = crypto_spawn_cipher(&ictx->cipher);	if (IS_ERR(cipher))		return PTR_ERR(cipher); 	ctr = crypto_spawn_skcipher(&ictx->ctr);	err = PTR_ERR(ctr);	if (IS_ERR(ctr))		goto err_free_cipher; 	ctx->cipher = cipher;	ctx->ctr = ctr; 	align = crypto_tfm_alg_alignmask(tfm);	align &= ~(crypto_tfm_ctx_alignment() - 1);	tfm->crt_aead.reqsize = align +				sizeof(struct crypto_ccm_req_priv_ctx) +				crypto_ablkcipher_reqsize(ctr); 	return 0; err_free_cipher:	crypto_free_cipher(cipher);	return err;} static void crypto_ccm_exit_tfm(struct crypto_tfm *tfm){	struct crypto_ccm_ctx *ctx = crypto_tfm_ctx(tfm); 	crypto_free_cipher(ctx->cipher);	crypto_free_ablkcipher(ctx->ctr);} static struct crypto_instance *crypto_ccm_alloc_common(struct rtattr **tb,						       const char *full_name,						       const char *ctr_name,						       const char *cipher_name){	struct crypto_attr_type *algt;	struct crypto_instance *inst;	struct crypto_alg *ctr;	struct crypto_alg *cipher;	struct ccm_instance_ctx *ictx;	int err; 	algt = crypto_get_attr_type(tb);	err = PTR_ERR(algt);	if (IS_ERR(algt))		return ERR_PTR(err); 	if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)		return ERR_PTR(-EINVAL); 	cipher = crypto_alg_mod_lookup(cipher_name,  CRYPTO_ALG_TYPE_CIPHER,				       CRYPTO_ALG_TYPE_MASK);	err = PTR_ERR(cipher);	if (IS_ERR(cipher))		return ERR_PTR(err); 	err = -EINVAL;

Lines 1–500 of 891.