diff options
Diffstat (limited to 'crypto/gcm.c')
| -rw-r--r-- | crypto/gcm.c | 116 | 
1 files changed, 97 insertions, 19 deletions
diff --git a/crypto/gcm.c b/crypto/gcm.c index 13ccbda34ff..43e1fb05ea5 100644 --- a/crypto/gcm.c +++ b/crypto/gcm.c @@ -37,8 +37,14 @@ 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];  }; @@ -1094,21 +1100,36 @@ static int crypto_rfc4543_setauthsize(struct crypto_aead *parent,  	return crypto_aead_setauthsize(ctx->child, authsize);  } +static void crypto_rfc4543_done(struct crypto_async_request *areq, int err) +{ +	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); +	} + +	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); @@ -1119,19 +1140,19 @@ 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); -	scatterwalk_crypto_chain(payload, dst, vdst == req->iv + 8, 2); +	scatterwalk_crypto_chain(payload, src, vsrc == req->iv + 8, 2);  	assoclen += 8 + req->cryptlen - (enc ? 0 : authsize);  	if (req->assoc->length == req->assoclen) { @@ -1150,14 +1171,27 @@ static struct aead_request *crypto_rfc4543_crypt(struct aead_request *req,  	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); @@ -1165,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; @@ -1178,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);  } @@ -1186,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); @@ -1205,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) @@ -1212,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) @@ -1220,6 +1282,7 @@ 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; @@ -1234,11 +1297,12 @@ static struct crypto_instance *crypto_rfc4543_alloc(struct rtattr **tb)  	if (IS_ERR(ccm_name))  		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)); @@ -1247,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, @@ -1263,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; @@ -1290,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: @@ -1300,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);  }  |