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