941 lines · 23.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * CCM: Counter with CBC-MAC * * (C) Copyright IBM Corp. 2007 - Joy Latten <latten@us.ibm.com> */ #include <crypto/internal/aead.h>#include <crypto/internal/cipher.h>#include <crypto/internal/hash.h>#include <crypto/internal/skcipher.h>#include <crypto/scatterwalk.h>#include <crypto/utils.h>#include <linux/err.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/string.h> struct ccm_instance_ctx {	struct crypto_skcipher_spawn ctr;	struct crypto_ahash_spawn mac;}; struct crypto_ccm_ctx {	struct crypto_ahash *mac;	struct crypto_skcipher *ctr;}; struct crypto_rfc4309_ctx {	struct crypto_aead *child;	u8 nonce[3];}; struct crypto_rfc4309_req_ctx {	struct scatterlist src[3];	struct scatterlist dst[3];	struct aead_request subreq;}; struct crypto_ccm_req_priv_ctx {	u8 odata[16];	u8 idata[16];	u8 auth_tag[16];	u32 flags;	struct scatterlist src[3];	struct scatterlist dst[3];	union {		struct ahash_request ahreq;		struct skcipher_request skreq;	};}; struct cbcmac_tfm_ctx {	struct crypto_cipher *child;}; 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_skcipher *ctr = ctx->ctr;	struct crypto_ahash *mac = ctx->mac;	int err; 	crypto_skcipher_clear_flags(ctr, CRYPTO_TFM_REQ_MASK);	crypto_skcipher_set_flags(ctr, crypto_aead_get_flags(aead) &				       CRYPTO_TFM_REQ_MASK);	err = crypto_skcipher_setkey(ctr, key, keylen);	if (err)		return err; 	crypto_ahash_clear_flags(mac, CRYPTO_TFM_REQ_MASK);	crypto_ahash_set_flags(mac, crypto_aead_get_flags(aead) &				    CRYPTO_TFM_REQ_MASK);	return crypto_ahash_setkey(mac, key, keylen);} 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 int crypto_ccm_auth(struct aead_request *req, struct scatterlist *plain,			   unsigned int cryptlen){	struct crypto_ccm_req_priv_ctx *pctx = crypto_ccm_reqctx(req);	struct crypto_aead *aead = crypto_aead_reqtfm(req);	struct crypto_ccm_ctx *ctx = crypto_aead_ctx(aead);	struct ahash_request *ahreq = &pctx->ahreq;	unsigned int assoclen = req->assoclen;	struct scatterlist sg[3];	u8 *odata = pctx->odata;	u8 *idata = pctx->idata;	int ilen, err; 	/* format control data for input */	err = format_input(odata, req, cryptlen);	if (err)		goto out; 	sg_init_table(sg, 3);	sg_set_buf(&sg[0], odata, 16); 	/* format associated data and compute into mac */	if (assoclen) {		ilen = format_adata(idata, assoclen);		sg_set_buf(&sg[1], idata, ilen);		sg_chain(sg, 3, req->src);	} else {		ilen = 0;		sg_chain(sg, 2, req->src);	} 	ahash_request_set_tfm(ahreq, ctx->mac);	ahash_request_set_callback(ahreq, pctx->flags, NULL, NULL);	ahash_request_set_crypt(ahreq, sg, NULL, assoclen + ilen + 16);	err = crypto_ahash_init(ahreq);	if (err)		goto out;	err = crypto_ahash_update(ahreq);	if (err)		goto out; 	/* we need to pad the MAC input to a round multiple of the block size */	ilen = 16 - (assoclen + ilen) % 16;	if (ilen < 16) {		memset(idata, 0, ilen);		sg_init_table(sg, 2);		sg_set_buf(&sg[0], idata, ilen);		if (plain)			sg_chain(sg, 2, plain);		plain = sg;		cryptlen += ilen;	} 	ahash_request_set_crypt(ahreq, plain, odata, cryptlen);	err = crypto_ahash_finup(ahreq);out:	return err;} static void crypto_ccm_encrypt_done(void *data, int err){	struct aead_request *req = 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->assoclen + 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_init_crypt(struct aead_request *req, u8 *tag){	struct crypto_ccm_req_priv_ctx *pctx = crypto_ccm_reqctx(req);	struct scatterlist *sg;	u8 *iv = req->iv;	int err; 	err = crypto_ccm_check_iv(iv);	if (err)		return err; 	pctx->flags = aead_request_flags(req); 	 /* 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, 3);	sg_set_buf(pctx->src, tag, 16);	sg = scatterwalk_ffwd(pctx->src + 1, req->src, req->assoclen);	if (sg != pctx->src + 1)		sg_chain(pctx->src, 2, sg); 	if (req->src != req->dst) {		sg_init_table(pctx->dst, 3);		sg_set_buf(pctx->dst, tag, 16);		sg = scatterwalk_ffwd(pctx->dst + 1, req->dst, req->assoclen);		if (sg != pctx->dst + 1)			sg_chain(pctx->dst, 2, sg);	} 	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 skcipher_request *skreq = &pctx->skreq;	struct scatterlist *dst;	unsigned int cryptlen = req->cryptlen;	u8 *odata = pctx->odata;	u8 *iv = req->iv;	int err; 	err = crypto_ccm_init_crypt(req, odata);	if (err)		return err; 	err = crypto_ccm_auth(req, sg_next(pctx->src), cryptlen);	if (err)		return err; 	dst = pctx->src;	if (req->src != req->dst)		dst = pctx->dst; 	skcipher_request_set_tfm(skreq, ctx->ctr);	skcipher_request_set_callback(skreq, pctx->flags,				      crypto_ccm_encrypt_done, req);	skcipher_request_set_crypt(skreq, pctx->src, dst, cryptlen + 16, iv);	err = crypto_skcipher_encrypt(skreq);	if (err)		return err; 	/* copy authtag to end of dst */	scatterwalk_map_and_copy(odata, sg_next(dst), cryptlen,				 crypto_aead_authsize(aead), 1);	return err;} static void crypto_ccm_decrypt_done(void *data, int err){	struct aead_request *req = 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;	struct scatterlist *dst; 	pctx->flags = 0; 	dst = sg_next(req->src == req->dst ? pctx->src : pctx->dst); 	if (!err) {		err = crypto_ccm_auth(req, dst, cryptlen);		if (!err && crypto_memneq(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 skcipher_request *skreq = &pctx->skreq;	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 = pctx->idata;	int err; 	cryptlen -= authsize; 	err = crypto_ccm_init_crypt(req, authtag);	if (err)		return err; 	scatterwalk_map_and_copy(authtag, sg_next(pctx->src), cryptlen,				 authsize, 0); 	dst = pctx->src;	if (req->src != req->dst)		dst = pctx->dst; 	memcpy(iv, req->iv, 16); 	skcipher_request_set_tfm(skreq, ctx->ctr);	skcipher_request_set_callback(skreq, pctx->flags,				      crypto_ccm_decrypt_done, req);	skcipher_request_set_crypt(skreq, pctx->src, dst, cryptlen + 16, iv);	err = crypto_skcipher_decrypt(skreq);	if (err)		return err; 	err = crypto_ccm_auth(req, sg_next(dst), cryptlen);	if (err)		return err; 	/* verify */	if (crypto_memneq(authtag, odata, authsize))		return -EBADMSG; 	return err;} static int crypto_ccm_init_tfm(struct crypto_aead *tfm){	struct aead_instance *inst = aead_alg_instance(tfm);	struct ccm_instance_ctx *ictx = aead_instance_ctx(inst);	struct crypto_ccm_ctx *ctx = crypto_aead_ctx(tfm);	struct crypto_ahash *mac;	struct crypto_skcipher *ctr;	unsigned long align;	int err; 	mac = crypto_spawn_ahash(&ictx->mac);	if (IS_ERR(mac))		return PTR_ERR(mac); 	ctr = crypto_spawn_skcipher(&ictx->ctr);	err = PTR_ERR(ctr);	if (IS_ERR(ctr))		goto err_free_mac; 	ctx->mac = mac;	ctx->ctr = ctr; 	align = crypto_aead_alignmask(tfm);	align &= ~(crypto_tfm_ctx_alignment() - 1);	crypto_aead_set_reqsize(		tfm,		align + sizeof(struct crypto_ccm_req_priv_ctx) +		max(crypto_ahash_reqsize(mac), crypto_skcipher_reqsize(ctr))); 	return 0; err_free_mac:	crypto_free_ahash(mac);	return err;} static void crypto_ccm_exit_tfm(struct crypto_aead *tfm){	struct crypto_ccm_ctx *ctx = crypto_aead_ctx(tfm); 	crypto_free_ahash(ctx->mac);	crypto_free_skcipher(ctx->ctr);} static void crypto_ccm_free(struct aead_instance *inst){	struct ccm_instance_ctx *ctx = aead_instance_ctx(inst); 	crypto_drop_ahash(&ctx->mac);	crypto_drop_skcipher(&ctx->ctr);	kfree(inst);} static int crypto_ccm_create_common(struct crypto_template *tmpl,				    struct rtattr **tb,				    const char *ctr_name,				    const char *mac_name){	struct skcipher_alg_common *ctr;	u32 mask;	struct aead_instance *inst;	struct ccm_instance_ctx *ictx;	struct hash_alg_common *mac;	int err; 	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_AEAD, &mask);	if (err)		return err; 	inst = kzalloc(sizeof(*inst) + sizeof(*ictx), GFP_KERNEL);	if (!inst)		return -ENOMEM;	ictx = aead_instance_ctx(inst); 	err = crypto_grab_ahash(&ictx->mac, aead_crypto_instance(inst),				mac_name, 0, mask | CRYPTO_ALG_ASYNC);	if (err)		goto err_free_inst;	mac = crypto_spawn_ahash_alg(&ictx->mac); 	err = -EINVAL;	if (strncmp(mac->base.cra_name, "cbcmac(", 7) != 0 ||	    mac->digestsize != 16)		goto err_free_inst; 	err = crypto_grab_skcipher(&ictx->ctr, aead_crypto_instance(inst),				   ctr_name, 0, mask);	if (err)		goto err_free_inst;	ctr = crypto_spawn_skcipher_alg_common(&ictx->ctr); 	/* The skcipher algorithm must be CTR mode, using 16-byte blocks. */	err = -EINVAL;	if (strncmp(ctr->base.cra_name, "ctr(", 4) != 0 ||	    ctr->ivsize != 16 || ctr->base.cra_blocksize != 1)		goto err_free_inst; 	/* ctr and cbcmac must use the same underlying block cipher. */	if (strcmp(ctr->base.cra_name + 4, mac->base.cra_name + 7) != 0)		goto err_free_inst; 	err = -ENAMETOOLONG;	if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,		     "ccm(%s", ctr->base.cra_name + 4) >= CRYPTO_MAX_ALG_NAME)		goto err_free_inst; 	if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,		     "ccm_base(%s,%s)", ctr->base.cra_driver_name,		     mac->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME)		goto err_free_inst; 	inst->alg.base.cra_priority = (mac->base.cra_priority +

Lines 1–500 of 941.