diff options
Diffstat (limited to 'crypto/gcm.c')
| -rw-r--r-- | crypto/gcm.c | 171 | 
1 files changed, 117 insertions, 54 deletions
diff --git a/crypto/gcm.c b/crypto/gcm.c index 2f5fbba6576..b4f01793900 100644 --- a/crypto/gcm.c +++ b/crypto/gcm.c @@ -37,13 +37,20 @@ struct crypto_rfc4106_ctx {  	u8 nonce[4];  }; +struct crypto_rfc4543_instance_ctx { +	struct crypto_aead_spawn aead; +	struct crypto_skcipher_spawn null; +}; +  struct crypto_rfc4543_ctx {  	struct crypto_aead *child; +	struct crypto_blkcipher *null;  	u8 nonce[4];  };  struct crypto_rfc4543_req_ctx {  	u8 auth_tag[16]; +	u8 assocbuf[32];  	struct scatterlist cipher[1];  	struct scatterlist payload[2];  	struct scatterlist assoc[2]; @@ -575,7 +582,7 @@ static int crypto_gcm_verify(struct aead_request *req,  	crypto_xor(auth_tag, iauth_tag, 16);  	scatterwalk_map_and_copy(iauth_tag, req->src, cryptlen, authsize, 0); -	return memcmp(iauth_tag, auth_tag, authsize) ? -EBADMSG : 0; +	return crypto_memneq(iauth_tag, auth_tag, authsize) ? -EBADMSG : 0;  }  static void gcm_decrypt_done(struct crypto_async_request *areq, int err) @@ -701,9 +708,8 @@ static struct crypto_instance *crypto_gcm_alloc_common(struct rtattr **tb,  	int err;  	algt = crypto_get_attr_type(tb); -	err = PTR_ERR(algt);  	if (IS_ERR(algt)) -		return ERR_PTR(err); +		return ERR_CAST(algt);  	if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)  		return ERR_PTR(-EINVAL); @@ -711,9 +717,8 @@ static struct crypto_instance *crypto_gcm_alloc_common(struct rtattr **tb,  	ghash_alg = crypto_find_alg(ghash_name, &crypto_ahash_type,  				    CRYPTO_ALG_TYPE_HASH,  				    CRYPTO_ALG_TYPE_AHASH_MASK); -	err = PTR_ERR(ghash_alg);  	if (IS_ERR(ghash_alg)) -		return ERR_PTR(err); +		return ERR_CAST(ghash_alg);  	err = -ENOMEM;  	inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL); @@ -787,15 +792,13 @@ out_put_ghash:  static struct crypto_instance *crypto_gcm_alloc(struct rtattr **tb)  { -	int err;  	const char *cipher_name;  	char ctr_name[CRYPTO_MAX_ALG_NAME];  	char full_name[CRYPTO_MAX_ALG_NAME];  	cipher_name = crypto_attr_alg_name(tb[1]); -	err = PTR_ERR(cipher_name);  	if (IS_ERR(cipher_name)) -		return ERR_PTR(err); +		return ERR_CAST(cipher_name);  	if (snprintf(ctr_name, CRYPTO_MAX_ALG_NAME, "ctr(%s)", cipher_name) >=  	    CRYPTO_MAX_ALG_NAME) @@ -826,20 +829,17 @@ static struct crypto_template crypto_gcm_tmpl = {  static struct crypto_instance *crypto_gcm_base_alloc(struct rtattr **tb)  { -	int err;  	const char *ctr_name;  	const char *ghash_name;  	char full_name[CRYPTO_MAX_ALG_NAME];  	ctr_name = crypto_attr_alg_name(tb[1]); -	err = PTR_ERR(ctr_name);  	if (IS_ERR(ctr_name)) -		return ERR_PTR(err); +		return ERR_CAST(ctr_name);  	ghash_name = crypto_attr_alg_name(tb[2]); -	err = PTR_ERR(ghash_name);  	if (IS_ERR(ghash_name)) -		return ERR_PTR(err); +		return ERR_CAST(ghash_name);  	if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "gcm_base(%s,%s)",  		     ctr_name, ghash_name) >= CRYPTO_MAX_ALG_NAME) @@ -971,17 +971,15 @@ static struct crypto_instance *crypto_rfc4106_alloc(struct rtattr **tb)  	int err;  	algt = crypto_get_attr_type(tb); -	err = PTR_ERR(algt);  	if (IS_ERR(algt)) -		return ERR_PTR(err); +		return ERR_CAST(algt);  	if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)  		return ERR_PTR(-EINVAL);  	ccm_name = crypto_attr_alg_name(tb[1]); -	err = PTR_ERR(ccm_name);  	if (IS_ERR(ccm_name)) -		return ERR_PTR(err); +		return ERR_CAST(ccm_name);  	inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL);  	if (!inst) @@ -1102,36 +1100,36 @@ static int crypto_rfc4543_setauthsize(struct crypto_aead *parent,  	return crypto_aead_setauthsize(ctx->child, authsize);  } -/* this is the same as crypto_authenc_chain */ -static void crypto_rfc4543_chain(struct scatterlist *head, -				 struct scatterlist *sg, int chain) +static void crypto_rfc4543_done(struct crypto_async_request *areq, int err)  { -	if (chain) { -		head->length += sg->length; -		sg = scatterwalk_sg_next(sg); +	struct aead_request *req = areq->data; +	struct crypto_aead *aead = crypto_aead_reqtfm(req); +	struct crypto_rfc4543_req_ctx *rctx = crypto_rfc4543_reqctx(req); + +	if (!err) { +		scatterwalk_map_and_copy(rctx->auth_tag, req->dst, +					 req->cryptlen, +					 crypto_aead_authsize(aead), 1);  	} -	if (sg) -		scatterwalk_sg_chain(head, 2, sg); -	else -		sg_mark_end(head); +	aead_request_complete(req, err);  }  static struct aead_request *crypto_rfc4543_crypt(struct aead_request *req, -						 int enc) +						 bool enc)  {  	struct crypto_aead *aead = crypto_aead_reqtfm(req);  	struct crypto_rfc4543_ctx *ctx = crypto_aead_ctx(aead);  	struct crypto_rfc4543_req_ctx *rctx = crypto_rfc4543_reqctx(req);  	struct aead_request *subreq = &rctx->subreq; -	struct scatterlist *dst = req->dst; +	struct scatterlist *src = req->src;  	struct scatterlist *cipher = rctx->cipher;  	struct scatterlist *payload = rctx->payload;  	struct scatterlist *assoc = rctx->assoc;  	unsigned int authsize = crypto_aead_authsize(aead);  	unsigned int assoclen = req->assoclen; -	struct page *dstp; -	u8 *vdst; +	struct page *srcp; +	u8 *vsrc;  	u8 *iv = PTR_ALIGN((u8 *)(rctx + 1) + crypto_aead_reqsize(ctx->child),  			   crypto_aead_alignmask(ctx->child) + 1); @@ -1142,35 +1140,58 @@ static struct aead_request *crypto_rfc4543_crypt(struct aead_request *req,  	if (enc)  		memset(rctx->auth_tag, 0, authsize);  	else -		scatterwalk_map_and_copy(rctx->auth_tag, dst, +		scatterwalk_map_and_copy(rctx->auth_tag, src,  					 req->cryptlen - authsize,  					 authsize, 0);  	sg_init_one(cipher, rctx->auth_tag, authsize);  	/* construct the aad */ -	dstp = sg_page(dst); -	vdst = PageHighMem(dstp) ? NULL : page_address(dstp) + dst->offset; +	srcp = sg_page(src); +	vsrc = PageHighMem(srcp) ? NULL : page_address(srcp) + src->offset;  	sg_init_table(payload, 2);  	sg_set_buf(payload, req->iv, 8); -	crypto_rfc4543_chain(payload, dst, vdst == req->iv + 8); +	scatterwalk_crypto_chain(payload, src, vsrc == req->iv + 8, 2);  	assoclen += 8 + req->cryptlen - (enc ? 0 : authsize); -	sg_init_table(assoc, 2); -	sg_set_page(assoc, sg_page(req->assoc), req->assoc->length, -		    req->assoc->offset); -	crypto_rfc4543_chain(assoc, payload, 0); +	if (req->assoc->length == req->assoclen) { +		sg_init_table(assoc, 2); +		sg_set_page(assoc, sg_page(req->assoc), req->assoc->length, +			    req->assoc->offset); +	} else { +		BUG_ON(req->assoclen > sizeof(rctx->assocbuf)); + +		scatterwalk_map_and_copy(rctx->assocbuf, req->assoc, 0, +					 req->assoclen, 0); + +		sg_init_table(assoc, 2); +		sg_set_buf(assoc, rctx->assocbuf, req->assoclen); +	} +	scatterwalk_crypto_chain(assoc, payload, 0, 2);  	aead_request_set_tfm(subreq, ctx->child); -	aead_request_set_callback(subreq, req->base.flags, req->base.complete, -				  req->base.data); +	aead_request_set_callback(subreq, req->base.flags, crypto_rfc4543_done, +				  req);  	aead_request_set_crypt(subreq, cipher, cipher, enc ? 0 : authsize, iv);  	aead_request_set_assoc(subreq, assoc, assoclen);  	return subreq;  } +static int crypto_rfc4543_copy_src_to_dst(struct aead_request *req, bool enc) +{ +	struct crypto_aead *aead = crypto_aead_reqtfm(req); +	struct crypto_rfc4543_ctx *ctx = crypto_aead_ctx(aead); +	unsigned int authsize = crypto_aead_authsize(aead); +	unsigned int nbytes = req->cryptlen - (enc ? 0 : authsize); +	struct blkcipher_desc desc = { +		.tfm = ctx->null, +	}; + +	return crypto_blkcipher_encrypt(&desc, req->dst, req->src, nbytes); +} +  static int crypto_rfc4543_encrypt(struct aead_request *req)  {  	struct crypto_aead *aead = crypto_aead_reqtfm(req); @@ -1178,7 +1199,13 @@ static int crypto_rfc4543_encrypt(struct aead_request *req)  	struct aead_request *subreq;  	int err; -	subreq = crypto_rfc4543_crypt(req, 1); +	if (req->src != req->dst) { +		err = crypto_rfc4543_copy_src_to_dst(req, true); +		if (err) +			return err; +	} + +	subreq = crypto_rfc4543_crypt(req, true);  	err = crypto_aead_encrypt(subreq);  	if (err)  		return err; @@ -1191,7 +1218,15 @@ static int crypto_rfc4543_encrypt(struct aead_request *req)  static int crypto_rfc4543_decrypt(struct aead_request *req)  { -	req = crypto_rfc4543_crypt(req, 0); +	int err; + +	if (req->src != req->dst) { +		err = crypto_rfc4543_copy_src_to_dst(req, false); +		if (err) +			return err; +	} + +	req = crypto_rfc4543_crypt(req, false);  	return crypto_aead_decrypt(req);  } @@ -1199,16 +1234,25 @@ static int crypto_rfc4543_decrypt(struct aead_request *req)  static int crypto_rfc4543_init_tfm(struct crypto_tfm *tfm)  {  	struct crypto_instance *inst = (void *)tfm->__crt_alg; -	struct crypto_aead_spawn *spawn = crypto_instance_ctx(inst); +	struct crypto_rfc4543_instance_ctx *ictx = crypto_instance_ctx(inst); +	struct crypto_aead_spawn *spawn = &ictx->aead;  	struct crypto_rfc4543_ctx *ctx = crypto_tfm_ctx(tfm);  	struct crypto_aead *aead; +	struct crypto_blkcipher *null;  	unsigned long align; +	int err = 0;  	aead = crypto_spawn_aead(spawn);  	if (IS_ERR(aead))  		return PTR_ERR(aead); +	null = crypto_spawn_blkcipher(&ictx->null.base); +	err = PTR_ERR(null); +	if (IS_ERR(null)) +		goto err_free_aead; +  	ctx->child = aead; +	ctx->null = null;  	align = crypto_aead_alignmask(aead);  	align &= ~(crypto_tfm_ctx_alignment() - 1); @@ -1218,6 +1262,10 @@ static int crypto_rfc4543_init_tfm(struct crypto_tfm *tfm)  				align + 16;  	return 0; + +err_free_aead: +	crypto_free_aead(aead); +	return err;  }  static void crypto_rfc4543_exit_tfm(struct crypto_tfm *tfm) @@ -1225,6 +1273,7 @@ static void crypto_rfc4543_exit_tfm(struct crypto_tfm *tfm)  	struct crypto_rfc4543_ctx *ctx = crypto_tfm_ctx(tfm);  	crypto_free_aead(ctx->child); +	crypto_free_blkcipher(ctx->null);  }  static struct crypto_instance *crypto_rfc4543_alloc(struct rtattr **tb) @@ -1233,27 +1282,27 @@ static struct crypto_instance *crypto_rfc4543_alloc(struct rtattr **tb)  	struct crypto_instance *inst;  	struct crypto_aead_spawn *spawn;  	struct crypto_alg *alg; +	struct crypto_rfc4543_instance_ctx *ctx;  	const char *ccm_name;  	int err;  	algt = crypto_get_attr_type(tb); -	err = PTR_ERR(algt);  	if (IS_ERR(algt)) -		return ERR_PTR(err); +		return ERR_CAST(algt);  	if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)  		return ERR_PTR(-EINVAL);  	ccm_name = crypto_attr_alg_name(tb[1]); -	err = PTR_ERR(ccm_name);  	if (IS_ERR(ccm_name)) -		return ERR_PTR(err); +		return ERR_CAST(ccm_name); -	inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL); +	inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);  	if (!inst)  		return ERR_PTR(-ENOMEM); -	spawn = crypto_instance_ctx(inst); +	ctx = crypto_instance_ctx(inst); +	spawn = &ctx->aead;  	crypto_set_aead_spawn(spawn, inst);  	err = crypto_grab_aead(spawn, ccm_name, 0,  			       crypto_requires_sync(algt->type, algt->mask)); @@ -1262,15 +1311,23 @@ static struct crypto_instance *crypto_rfc4543_alloc(struct rtattr **tb)  	alg = crypto_aead_spawn_alg(spawn); +	crypto_set_skcipher_spawn(&ctx->null, inst); +	err = crypto_grab_skcipher(&ctx->null, "ecb(cipher_null)", 0, +				   CRYPTO_ALG_ASYNC); +	if (err) +		goto out_drop_alg; + +	crypto_skcipher_spawn_alg(&ctx->null); +  	err = -EINVAL;  	/* We only support 16-byte blocks. */  	if (alg->cra_aead.ivsize != 16) -		goto out_drop_alg; +		goto out_drop_ecbnull;  	/* Not a stream cipher? */  	if (alg->cra_blocksize != 1) -		goto out_drop_alg; +		goto out_drop_ecbnull;  	err = -ENAMETOOLONG;  	if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME, @@ -1278,7 +1335,7 @@ static struct crypto_instance *crypto_rfc4543_alloc(struct rtattr **tb)  	    snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,  		     "rfc4543(%s)", alg->cra_driver_name) >=  	    CRYPTO_MAX_ALG_NAME) -		goto out_drop_alg; +		goto out_drop_ecbnull;  	inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD;  	inst->alg.cra_flags |= alg->cra_flags & CRYPTO_ALG_ASYNC; @@ -1305,6 +1362,8 @@ static struct crypto_instance *crypto_rfc4543_alloc(struct rtattr **tb)  out:  	return inst; +out_drop_ecbnull: +	crypto_drop_skcipher(&ctx->null);  out_drop_alg:  	crypto_drop_aead(spawn);  out_free_inst: @@ -1315,7 +1374,11 @@ out_free_inst:  static void crypto_rfc4543_free(struct crypto_instance *inst)  { -	crypto_drop_spawn(crypto_instance_ctx(inst)); +	struct crypto_rfc4543_instance_ctx *ctx = crypto_instance_ctx(inst); + +	crypto_drop_aead(&ctx->aead); +	crypto_drop_skcipher(&ctx->null); +  	kfree(inst);  }  | 
