brintos

brintos / linux-shallow public Read only

0
0
Text · 13.6 KiB · 2cc933e Raw
475 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * authencesn.c - AEAD wrapper for IPsec with extended sequence numbers,4 *                 derived from authenc.c5 *6 * Copyright (C) 2010 secunet Security Networks AG7 * Copyright (C) 2010 Steffen Klassert <steffen.klassert@secunet.com>8 * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>9 */10 11#include <crypto/internal/aead.h>12#include <crypto/internal/hash.h>13#include <crypto/internal/skcipher.h>14#include <crypto/authenc.h>15#include <crypto/null.h>16#include <crypto/scatterwalk.h>17#include <linux/err.h>18#include <linux/init.h>19#include <linux/kernel.h>20#include <linux/module.h>21#include <linux/rtnetlink.h>22#include <linux/slab.h>23#include <linux/spinlock.h>24 25struct authenc_esn_instance_ctx {26	struct crypto_ahash_spawn auth;27	struct crypto_skcipher_spawn enc;28};29 30struct crypto_authenc_esn_ctx {31	unsigned int reqoff;32	struct crypto_ahash *auth;33	struct crypto_skcipher *enc;34	struct crypto_sync_skcipher *null;35};36 37struct authenc_esn_request_ctx {38	struct scatterlist src[2];39	struct scatterlist dst[2];40	char tail[];41};42 43static void authenc_esn_request_complete(struct aead_request *req, int err)44{45	if (err != -EINPROGRESS)46		aead_request_complete(req, err);47}48 49static int crypto_authenc_esn_setauthsize(struct crypto_aead *authenc_esn,50					  unsigned int authsize)51{52	if (authsize > 0 && authsize < 4)53		return -EINVAL;54 55	return 0;56}57 58static int crypto_authenc_esn_setkey(struct crypto_aead *authenc_esn, const u8 *key,59				     unsigned int keylen)60{61	struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn);62	struct crypto_ahash *auth = ctx->auth;63	struct crypto_skcipher *enc = ctx->enc;64	struct crypto_authenc_keys keys;65	int err = -EINVAL;66 67	if (crypto_authenc_extractkeys(&keys, key, keylen) != 0)68		goto out;69 70	crypto_ahash_clear_flags(auth, CRYPTO_TFM_REQ_MASK);71	crypto_ahash_set_flags(auth, crypto_aead_get_flags(authenc_esn) &72				     CRYPTO_TFM_REQ_MASK);73	err = crypto_ahash_setkey(auth, keys.authkey, keys.authkeylen);74	if (err)75		goto out;76 77	crypto_skcipher_clear_flags(enc, CRYPTO_TFM_REQ_MASK);78	crypto_skcipher_set_flags(enc, crypto_aead_get_flags(authenc_esn) &79					 CRYPTO_TFM_REQ_MASK);80	err = crypto_skcipher_setkey(enc, keys.enckey, keys.enckeylen);81out:82	memzero_explicit(&keys, sizeof(keys));83	return err;84}85 86static int crypto_authenc_esn_genicv_tail(struct aead_request *req,87					  unsigned int flags)88{89	struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req);90	struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req);91	u8 *hash = areq_ctx->tail;92	unsigned int authsize = crypto_aead_authsize(authenc_esn);93	unsigned int assoclen = req->assoclen;94	unsigned int cryptlen = req->cryptlen;95	struct scatterlist *dst = req->dst;96	u32 tmp[2];97 98	/* Move high-order bits of sequence number back. */99	scatterwalk_map_and_copy(tmp, dst, 4, 4, 0);100	scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0);101	scatterwalk_map_and_copy(tmp, dst, 0, 8, 1);102 103	scatterwalk_map_and_copy(hash, dst, assoclen + cryptlen, authsize, 1);104	return 0;105}106 107static void authenc_esn_geniv_ahash_done(void *data, int err)108{109	struct aead_request *req = data;110 111	err = err ?: crypto_authenc_esn_genicv_tail(req, 0);112	aead_request_complete(req, err);113}114 115static int crypto_authenc_esn_genicv(struct aead_request *req,116				     unsigned int flags)117{118	struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req);119	struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req);120	struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn);121	struct crypto_ahash *auth = ctx->auth;122	u8 *hash = areq_ctx->tail;123	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff);124	unsigned int authsize = crypto_aead_authsize(authenc_esn);125	unsigned int assoclen = req->assoclen;126	unsigned int cryptlen = req->cryptlen;127	struct scatterlist *dst = req->dst;128	u32 tmp[2];129 130	if (!authsize)131		return 0;132 133	/* Move high-order bits of sequence number to the end. */134	scatterwalk_map_and_copy(tmp, dst, 0, 8, 0);135	scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);136	scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);137 138	sg_init_table(areq_ctx->dst, 2);139	dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4);140 141	ahash_request_set_tfm(ahreq, auth);142	ahash_request_set_crypt(ahreq, dst, hash, assoclen + cryptlen);143	ahash_request_set_callback(ahreq, flags,144				   authenc_esn_geniv_ahash_done, req);145 146	return crypto_ahash_digest(ahreq) ?:147	       crypto_authenc_esn_genicv_tail(req, aead_request_flags(req));148}149 150 151static void crypto_authenc_esn_encrypt_done(void *data, int err)152{153	struct aead_request *areq = data;154 155	if (!err)156		err = crypto_authenc_esn_genicv(areq, 0);157 158	authenc_esn_request_complete(areq, err);159}160 161static int crypto_authenc_esn_copy(struct aead_request *req, unsigned int len)162{163	struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req);164	struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn);165	SYNC_SKCIPHER_REQUEST_ON_STACK(skreq, ctx->null);166 167	skcipher_request_set_sync_tfm(skreq, ctx->null);168	skcipher_request_set_callback(skreq, aead_request_flags(req),169				      NULL, NULL);170	skcipher_request_set_crypt(skreq, req->src, req->dst, len, NULL);171 172	return crypto_skcipher_encrypt(skreq);173}174 175static int crypto_authenc_esn_encrypt(struct aead_request *req)176{177	struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req);178	struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req);179	struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn);180	struct skcipher_request *skreq = (void *)(areq_ctx->tail +181						  ctx->reqoff);182	struct crypto_skcipher *enc = ctx->enc;183	unsigned int assoclen = req->assoclen;184	unsigned int cryptlen = req->cryptlen;185	struct scatterlist *src, *dst;186	int err;187 188	sg_init_table(areq_ctx->src, 2);189	src = scatterwalk_ffwd(areq_ctx->src, req->src, assoclen);190	dst = src;191 192	if (req->src != req->dst) {193		err = crypto_authenc_esn_copy(req, assoclen);194		if (err)195			return err;196 197		sg_init_table(areq_ctx->dst, 2);198		dst = scatterwalk_ffwd(areq_ctx->dst, req->dst, assoclen);199	}200 201	skcipher_request_set_tfm(skreq, enc);202	skcipher_request_set_callback(skreq, aead_request_flags(req),203				      crypto_authenc_esn_encrypt_done, req);204	skcipher_request_set_crypt(skreq, src, dst, cryptlen, req->iv);205 206	err = crypto_skcipher_encrypt(skreq);207	if (err)208		return err;209 210	return crypto_authenc_esn_genicv(req, aead_request_flags(req));211}212 213static int crypto_authenc_esn_decrypt_tail(struct aead_request *req,214					   unsigned int flags)215{216	struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req);217	unsigned int authsize = crypto_aead_authsize(authenc_esn);218	struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req);219	struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn);220	struct skcipher_request *skreq = (void *)(areq_ctx->tail +221						  ctx->reqoff);222	struct crypto_ahash *auth = ctx->auth;223	u8 *ohash = areq_ctx->tail;224	unsigned int cryptlen = req->cryptlen - authsize;225	unsigned int assoclen = req->assoclen;226	struct scatterlist *dst = req->dst;227	u8 *ihash = ohash + crypto_ahash_digestsize(auth);228	u32 tmp[2];229 230	if (!authsize)231		goto decrypt;232 233	/* Move high-order bits of sequence number back. */234	scatterwalk_map_and_copy(tmp, dst, 4, 4, 0);235	scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0);236	scatterwalk_map_and_copy(tmp, dst, 0, 8, 1);237 238	if (crypto_memneq(ihash, ohash, authsize))239		return -EBADMSG;240 241decrypt:242 243	sg_init_table(areq_ctx->dst, 2);244	dst = scatterwalk_ffwd(areq_ctx->dst, dst, assoclen);245 246	skcipher_request_set_tfm(skreq, ctx->enc);247	skcipher_request_set_callback(skreq, flags,248				      req->base.complete, req->base.data);249	skcipher_request_set_crypt(skreq, dst, dst, cryptlen, req->iv);250 251	return crypto_skcipher_decrypt(skreq);252}253 254static void authenc_esn_verify_ahash_done(void *data, int err)255{256	struct aead_request *req = data;257 258	err = err ?: crypto_authenc_esn_decrypt_tail(req, 0);259	authenc_esn_request_complete(req, err);260}261 262static int crypto_authenc_esn_decrypt(struct aead_request *req)263{264	struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req);265	struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req);266	struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn);267	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff);268	unsigned int authsize = crypto_aead_authsize(authenc_esn);269	struct crypto_ahash *auth = ctx->auth;270	u8 *ohash = areq_ctx->tail;271	unsigned int assoclen = req->assoclen;272	unsigned int cryptlen = req->cryptlen;273	u8 *ihash = ohash + crypto_ahash_digestsize(auth);274	struct scatterlist *dst = req->dst;275	u32 tmp[2];276	int err;277 278	cryptlen -= authsize;279 280	if (req->src != dst) {281		err = crypto_authenc_esn_copy(req, assoclen + cryptlen);282		if (err)283			return err;284	}285 286	scatterwalk_map_and_copy(ihash, req->src, assoclen + cryptlen,287				 authsize, 0);288 289	if (!authsize)290		goto tail;291 292	/* Move high-order bits of sequence number to the end. */293	scatterwalk_map_and_copy(tmp, dst, 0, 8, 0);294	scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);295	scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);296 297	sg_init_table(areq_ctx->dst, 2);298	dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4);299 300	ahash_request_set_tfm(ahreq, auth);301	ahash_request_set_crypt(ahreq, dst, ohash, assoclen + cryptlen);302	ahash_request_set_callback(ahreq, aead_request_flags(req),303				   authenc_esn_verify_ahash_done, req);304 305	err = crypto_ahash_digest(ahreq);306	if (err)307		return err;308 309tail:310	return crypto_authenc_esn_decrypt_tail(req, aead_request_flags(req));311}312 313static int crypto_authenc_esn_init_tfm(struct crypto_aead *tfm)314{315	struct aead_instance *inst = aead_alg_instance(tfm);316	struct authenc_esn_instance_ctx *ictx = aead_instance_ctx(inst);317	struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(tfm);318	struct crypto_ahash *auth;319	struct crypto_skcipher *enc;320	struct crypto_sync_skcipher *null;321	int err;322 323	auth = crypto_spawn_ahash(&ictx->auth);324	if (IS_ERR(auth))325		return PTR_ERR(auth);326 327	enc = crypto_spawn_skcipher(&ictx->enc);328	err = PTR_ERR(enc);329	if (IS_ERR(enc))330		goto err_free_ahash;331 332	null = crypto_get_default_null_skcipher();333	err = PTR_ERR(null);334	if (IS_ERR(null))335		goto err_free_skcipher;336 337	ctx->auth = auth;338	ctx->enc = enc;339	ctx->null = null;340 341	ctx->reqoff = 2 * crypto_ahash_digestsize(auth);342 343	crypto_aead_set_reqsize(344		tfm,345		sizeof(struct authenc_esn_request_ctx) +346		ctx->reqoff +347		max_t(unsigned int,348		      crypto_ahash_reqsize(auth) +349		      sizeof(struct ahash_request),350		      sizeof(struct skcipher_request) +351		      crypto_skcipher_reqsize(enc)));352 353	return 0;354 355err_free_skcipher:356	crypto_free_skcipher(enc);357err_free_ahash:358	crypto_free_ahash(auth);359	return err;360}361 362static void crypto_authenc_esn_exit_tfm(struct crypto_aead *tfm)363{364	struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(tfm);365 366	crypto_free_ahash(ctx->auth);367	crypto_free_skcipher(ctx->enc);368	crypto_put_default_null_skcipher();369}370 371static void crypto_authenc_esn_free(struct aead_instance *inst)372{373	struct authenc_esn_instance_ctx *ctx = aead_instance_ctx(inst);374 375	crypto_drop_skcipher(&ctx->enc);376	crypto_drop_ahash(&ctx->auth);377	kfree(inst);378}379 380static int crypto_authenc_esn_create(struct crypto_template *tmpl,381				     struct rtattr **tb)382{383	u32 mask;384	struct aead_instance *inst;385	struct authenc_esn_instance_ctx *ctx;386	struct skcipher_alg_common *enc;387	struct hash_alg_common *auth;388	struct crypto_alg *auth_base;389	int err;390 391	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_AEAD, &mask);392	if (err)393		return err;394 395	inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);396	if (!inst)397		return -ENOMEM;398	ctx = aead_instance_ctx(inst);399 400	err = crypto_grab_ahash(&ctx->auth, aead_crypto_instance(inst),401				crypto_attr_alg_name(tb[1]), 0, mask);402	if (err)403		goto err_free_inst;404	auth = crypto_spawn_ahash_alg(&ctx->auth);405	auth_base = &auth->base;406 407	err = crypto_grab_skcipher(&ctx->enc, aead_crypto_instance(inst),408				   crypto_attr_alg_name(tb[2]), 0, mask);409	if (err)410		goto err_free_inst;411	enc = crypto_spawn_skcipher_alg_common(&ctx->enc);412 413	err = -ENAMETOOLONG;414	if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,415		     "authencesn(%s,%s)", auth_base->cra_name,416		     enc->base.cra_name) >= CRYPTO_MAX_ALG_NAME)417		goto err_free_inst;418 419	if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,420		     "authencesn(%s,%s)", auth_base->cra_driver_name,421		     enc->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME)422		goto err_free_inst;423 424	inst->alg.base.cra_priority = enc->base.cra_priority * 10 +425				      auth_base->cra_priority;426	inst->alg.base.cra_blocksize = enc->base.cra_blocksize;427	inst->alg.base.cra_alignmask = enc->base.cra_alignmask;428	inst->alg.base.cra_ctxsize = sizeof(struct crypto_authenc_esn_ctx);429 430	inst->alg.ivsize = enc->ivsize;431	inst->alg.chunksize = enc->chunksize;432	inst->alg.maxauthsize = auth->digestsize;433 434	inst->alg.init = crypto_authenc_esn_init_tfm;435	inst->alg.exit = crypto_authenc_esn_exit_tfm;436 437	inst->alg.setkey = crypto_authenc_esn_setkey;438	inst->alg.setauthsize = crypto_authenc_esn_setauthsize;439	inst->alg.encrypt = crypto_authenc_esn_encrypt;440	inst->alg.decrypt = crypto_authenc_esn_decrypt;441 442	inst->free = crypto_authenc_esn_free;443 444	err = aead_register_instance(tmpl, inst);445	if (err) {446err_free_inst:447		crypto_authenc_esn_free(inst);448	}449	return err;450}451 452static struct crypto_template crypto_authenc_esn_tmpl = {453	.name = "authencesn",454	.create = crypto_authenc_esn_create,455	.module = THIS_MODULE,456};457 458static int __init crypto_authenc_esn_module_init(void)459{460	return crypto_register_template(&crypto_authenc_esn_tmpl);461}462 463static void __exit crypto_authenc_esn_module_exit(void)464{465	crypto_unregister_template(&crypto_authenc_esn_tmpl);466}467 468subsys_initcall(crypto_authenc_esn_module_init);469module_exit(crypto_authenc_esn_module_exit);470 471MODULE_LICENSE("GPL");472MODULE_AUTHOR("Steffen Klassert <steffen.klassert@secunet.com>");473MODULE_DESCRIPTION("AEAD wrapper for IPsec with extended sequence numbers");474MODULE_ALIAS_CRYPTO("authencesn");475