authenc.c 19.8 KB
Newer Older
Herbert Xu's avatar
Herbert Xu committed
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * Authenc: Simple AEAD wrapper for IPsec
 *
 * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 */

13
#include <crypto/aead.h>
14
#include <crypto/internal/hash.h>
15
#include <crypto/internal/skcipher.h>
16
#include <crypto/authenc.h>
17
#include <crypto/scatterwalk.h>
Herbert Xu's avatar
Herbert Xu committed
18 19 20 21
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
22
#include <linux/rtnetlink.h>
Herbert Xu's avatar
Herbert Xu committed
23 24 25
#include <linux/slab.h>
#include <linux/spinlock.h>

26 27
typedef u8 *(*authenc_ahash_t)(struct aead_request *req, unsigned int flags);

Herbert Xu's avatar
Herbert Xu committed
28
struct authenc_instance_ctx {
29
	struct crypto_ahash_spawn auth;
30
	struct crypto_skcipher_spawn enc;
Herbert Xu's avatar
Herbert Xu committed
31 32 33
};

struct crypto_authenc_ctx {
34 35
	unsigned int reqoff;
	struct crypto_ahash *auth;
Herbert Xu's avatar
Herbert Xu committed
36 37 38
	struct crypto_ablkcipher *enc;
};

39 40 41 42 43 44 45 46 47 48
struct authenc_request_ctx {
	unsigned int cryptlen;
	struct scatterlist *sg;
	struct scatterlist asg[2];
	struct scatterlist cipher[2];
	crypto_completion_t complete;
	crypto_completion_t update_complete;
	char tail[];
};

49 50 51 52 53 54
static void authenc_request_complete(struct aead_request *req, int err)
{
	if (err != -EINPROGRESS)
		aead_request_complete(req, err);
}

Herbert Xu's avatar
Herbert Xu committed
55 56 57 58
static int crypto_authenc_setkey(struct crypto_aead *authenc, const u8 *key,
				 unsigned int keylen)
{
	unsigned int authkeylen;
59
	unsigned int enckeylen;
Herbert Xu's avatar
Herbert Xu committed
60
	struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
61
	struct crypto_ahash *auth = ctx->auth;
Herbert Xu's avatar
Herbert Xu committed
62
	struct crypto_ablkcipher *enc = ctx->enc;
63 64
	struct rtattr *rta = (void *)key;
	struct crypto_authenc_key_param *param;
Herbert Xu's avatar
Herbert Xu committed
65 66
	int err = -EINVAL;

67
	if (!RTA_OK(rta, keylen))
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
		goto badkey;
	if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
		goto badkey;
	if (RTA_PAYLOAD(rta) < sizeof(*param))
		goto badkey;

	param = RTA_DATA(rta);
	enckeylen = be32_to_cpu(param->enckeylen);

	key += RTA_ALIGN(rta->rta_len);
	keylen -= RTA_ALIGN(rta->rta_len);

	if (keylen < enckeylen)
		goto badkey;

Herbert Xu's avatar
Herbert Xu committed
83 84
	authkeylen = keylen - enckeylen;

85 86
	crypto_ahash_clear_flags(auth, CRYPTO_TFM_REQ_MASK);
	crypto_ahash_set_flags(auth, crypto_aead_get_flags(authenc) &
Herbert Xu's avatar
Herbert Xu committed
87
				    CRYPTO_TFM_REQ_MASK);
88 89
	err = crypto_ahash_setkey(auth, key, authkeylen);
	crypto_aead_set_flags(authenc, crypto_ahash_get_flags(auth) &
Herbert Xu's avatar
Herbert Xu committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103
				       CRYPTO_TFM_RES_MASK);

	if (err)
		goto out;

	crypto_ablkcipher_clear_flags(enc, CRYPTO_TFM_REQ_MASK);
	crypto_ablkcipher_set_flags(enc, crypto_aead_get_flags(authenc) &
					 CRYPTO_TFM_REQ_MASK);
	err = crypto_ablkcipher_setkey(enc, key + authkeylen, enckeylen);
	crypto_aead_set_flags(authenc, crypto_ablkcipher_get_flags(enc) &
				       CRYPTO_TFM_RES_MASK);

out:
	return err;
104 105 106 107

badkey:
	crypto_aead_set_flags(authenc, CRYPTO_TFM_RES_BAD_KEY_LEN);
	goto out;
Herbert Xu's avatar
Herbert Xu committed
108 109
}

110 111 112 113 114 115 116 117 118 119 120 121 122 123
static void authenc_chain(struct scatterlist *head, struct scatterlist *sg,
			  int chain)
{
	if (chain) {
		head->length += sg->length;
		sg = scatterwalk_sg_next(sg);
	}

	if (sg)
		scatterwalk_sg_chain(head, 2, sg);
	else
		sg_mark_end(head);
}

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
static void authenc_geniv_ahash_update_done(struct crypto_async_request *areq,
					    int err)
{
	struct aead_request *req = areq->data;
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
	struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
	struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff);

	if (err)
		goto out;

	ahash_request_set_crypt(ahreq, areq_ctx->sg, ahreq->result,
				areq_ctx->cryptlen);
	ahash_request_set_callback(ahreq, aead_request_flags(req) &
					  CRYPTO_TFM_REQ_MAY_SLEEP,
				   areq_ctx->complete, req);

	err = crypto_ahash_finup(ahreq);
	if (err)
		goto out;

	scatterwalk_map_and_copy(ahreq->result, areq_ctx->sg,
				 areq_ctx->cryptlen,
				 crypto_aead_authsize(authenc), 1);

out:
151
	authenc_request_complete(req, err);
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
}

static void authenc_geniv_ahash_done(struct crypto_async_request *areq, int err)
{
	struct aead_request *req = areq->data;
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
	struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
	struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff);

	if (err)
		goto out;

	scatterwalk_map_and_copy(ahreq->result, areq_ctx->sg,
				 areq_ctx->cryptlen,
				 crypto_aead_authsize(authenc), 1);

out:
	aead_request_complete(req, err);
}

static void authenc_verify_ahash_update_done(struct crypto_async_request *areq,
					     int err)
Herbert Xu's avatar
Herbert Xu committed
175
{
176 177 178 179
	u8 *ihash;
	unsigned int authsize;
	struct ablkcipher_request *abreq;
	struct aead_request *req = areq->data;
Herbert Xu's avatar
Herbert Xu committed
180 181
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
	struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
182 183
	struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff);
184
	unsigned int cryptlen = req->cryptlen;
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199

	if (err)
		goto out;

	ahash_request_set_crypt(ahreq, areq_ctx->sg, ahreq->result,
				areq_ctx->cryptlen);
	ahash_request_set_callback(ahreq, aead_request_flags(req) &
					  CRYPTO_TFM_REQ_MAY_SLEEP,
				   areq_ctx->complete, req);

	err = crypto_ahash_finup(ahreq);
	if (err)
		goto out;

	authsize = crypto_aead_authsize(authenc);
200
	cryptlen -= authsize;
201 202 203 204
	ihash = ahreq->result + authsize;
	scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen,
				 authsize, 0);

205
	err = memcmp(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
206 207 208 209 210 211 212 213
	if (err)
		goto out;

	abreq = aead_request_ctx(req);
	ablkcipher_request_set_tfm(abreq, ctx->enc);
	ablkcipher_request_set_callback(abreq, aead_request_flags(req),
					req->base.complete, req->base.data);
	ablkcipher_request_set_crypt(abreq, req->src, req->dst,
214
				     cryptlen, req->iv);
215 216 217 218

	err = crypto_ablkcipher_decrypt(abreq);

out:
219
	authenc_request_complete(req, err);
220 221 222 223 224 225 226 227 228 229 230 231 232
}

static void authenc_verify_ahash_done(struct crypto_async_request *areq,
				      int err)
{
	u8 *ihash;
	unsigned int authsize;
	struct ablkcipher_request *abreq;
	struct aead_request *req = areq->data;
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
	struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
	struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff);
233
	unsigned int cryptlen = req->cryptlen;
234 235 236 237 238

	if (err)
		goto out;

	authsize = crypto_aead_authsize(authenc);
239
	cryptlen -= authsize;
240 241 242 243
	ihash = ahreq->result + authsize;
	scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen,
				 authsize, 0);

244
	err = memcmp(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
245 246 247 248 249 250 251 252
	if (err)
		goto out;

	abreq = aead_request_ctx(req);
	ablkcipher_request_set_tfm(abreq, ctx->enc);
	ablkcipher_request_set_callback(abreq, aead_request_flags(req),
					req->base.complete, req->base.data);
	ablkcipher_request_set_crypt(abreq, req->src, req->dst,
253
				     cryptlen, req->iv);
254 255 256 257

	err = crypto_ablkcipher_decrypt(abreq);

out:
258
	authenc_request_complete(req, err);
259 260 261 262 263 264 265 266 267 268
}

static u8 *crypto_authenc_ahash_fb(struct aead_request *req, unsigned int flags)
{
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
	struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
	struct crypto_ahash *auth = ctx->auth;
	struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff);
	u8 *hash = areq_ctx->tail;
Herbert Xu's avatar
Herbert Xu committed
269 270
	int err;

271 272 273 274
	hash = (u8 *)ALIGN((unsigned long)hash + crypto_ahash_alignmask(auth),
			    crypto_ahash_alignmask(auth) + 1);

	ahash_request_set_tfm(ahreq, auth);
Herbert Xu's avatar
Herbert Xu committed
275

276
	err = crypto_ahash_init(ahreq);
Herbert Xu's avatar
Herbert Xu committed
277
	if (err)
278 279 280 281 282
		return ERR_PTR(err);

	ahash_request_set_crypt(ahreq, req->assoc, hash, req->assoclen);
	ahash_request_set_callback(ahreq, aead_request_flags(req) & flags,
				   areq_ctx->update_complete, req);
Herbert Xu's avatar
Herbert Xu committed
283

284
	err = crypto_ahash_update(ahreq);
Herbert Xu's avatar
Herbert Xu committed
285
	if (err)
286 287 288 289 290 291
		return ERR_PTR(err);

	ahash_request_set_crypt(ahreq, areq_ctx->sg, hash,
				areq_ctx->cryptlen);
	ahash_request_set_callback(ahreq, aead_request_flags(req) & flags,
				   areq_ctx->complete, req);
Herbert Xu's avatar
Herbert Xu committed
292

293
	err = crypto_ahash_finup(ahreq);
Herbert Xu's avatar
Herbert Xu committed
294
	if (err)
295
		return ERR_PTR(err);
Herbert Xu's avatar
Herbert Xu committed
296

297 298 299 300 301 302 303 304 305 306 307 308
	return hash;
}

static u8 *crypto_authenc_ahash(struct aead_request *req, unsigned int flags)
{
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
	struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
	struct crypto_ahash *auth = ctx->auth;
	struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff);
	u8 *hash = areq_ctx->tail;
	int err;
Herbert Xu's avatar
Herbert Xu committed
309

310 311 312 313 314 315 316 317 318 319
	hash = (u8 *)ALIGN((unsigned long)hash + crypto_ahash_alignmask(auth),
			   crypto_ahash_alignmask(auth) + 1);

	ahash_request_set_tfm(ahreq, auth);
	ahash_request_set_crypt(ahreq, areq_ctx->sg, hash,
				areq_ctx->cryptlen);
	ahash_request_set_callback(ahreq, aead_request_flags(req) & flags,
				   areq_ctx->complete, req);

	err = crypto_ahash_digest(ahreq);
Herbert Xu's avatar
Herbert Xu committed
320
	if (err)
321 322 323 324 325
		return ERR_PTR(err);

	return hash;
}

326 327
static int crypto_authenc_genicv(struct aead_request *req, u8 *iv,
				 unsigned int flags)
328 329
{
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
330
	struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
331
	struct scatterlist *dst = req->dst;
332 333 334
	struct scatterlist *assoc = req->assoc;
	struct scatterlist *cipher = areq_ctx->cipher;
	struct scatterlist *asg = areq_ctx->asg;
335
	unsigned int ivsize = crypto_aead_ivsize(authenc);
336 337 338
	unsigned int cryptlen = req->cryptlen;
	authenc_ahash_t authenc_ahash_fn = crypto_authenc_ahash_fb;
	struct page *dstp;
339
	u8 *vdst;
340 341
	u8 *hash;

342 343 344
	dstp = sg_page(dst);
	vdst = PageHighMem(dstp) ? NULL : page_address(dstp) + dst->offset;

345 346 347 348 349
	if (ivsize) {
		sg_init_table(cipher, 2);
		sg_set_buf(cipher, iv, ivsize);
		authenc_chain(cipher, dst, vdst == iv + ivsize);
		dst = cipher;
350
		cryptlen += ivsize;
351
	}
352

353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
	if (sg_is_last(assoc)) {
		authenc_ahash_fn = crypto_authenc_ahash;
		sg_init_table(asg, 2);
		sg_set_page(asg, sg_page(assoc), assoc->length, assoc->offset);
		authenc_chain(asg, dst, 0);
		dst = asg;
		cryptlen += req->assoclen;
	}

	areq_ctx->cryptlen = cryptlen;
	areq_ctx->sg = dst;

	areq_ctx->complete = authenc_geniv_ahash_done;
	areq_ctx->update_complete = authenc_geniv_ahash_update_done;

	hash = authenc_ahash_fn(req, flags);
369 370
	if (IS_ERR(hash))
		return PTR_ERR(hash);
Herbert Xu's avatar
Herbert Xu committed
371

372
	scatterwalk_map_and_copy(hash, dst, cryptlen,
373
				 crypto_aead_authsize(authenc), 1);
Herbert Xu's avatar
Herbert Xu committed
374 375 376 377 378 379
	return 0;
}

static void crypto_authenc_encrypt_done(struct crypto_async_request *req,
					int err)
{
380 381
	struct aead_request *areq = req->data;

382 383 384 385 386 387 388 389 390
	if (!err) {
		struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
		struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
		struct ablkcipher_request *abreq = aead_request_ctx(areq);
		u8 *iv = (u8 *)(abreq + 1) +
			 crypto_ablkcipher_reqsize(ctx->enc);

		err = crypto_authenc_genicv(areq, iv, 0);
	}
Herbert Xu's avatar
Herbert Xu committed
391

392
	authenc_request_complete(areq, err);
Herbert Xu's avatar
Herbert Xu committed
393 394 395 396 397 398
}

static int crypto_authenc_encrypt(struct aead_request *req)
{
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
	struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
399
	struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
400 401 402
	struct crypto_ablkcipher *enc = ctx->enc;
	struct scatterlist *dst = req->dst;
	unsigned int cryptlen = req->cryptlen;
403 404 405
	struct ablkcipher_request *abreq = (void *)(areq_ctx->tail
						    + ctx->reqoff);
	u8 *iv = (u8 *)abreq - crypto_ablkcipher_ivsize(enc);
Herbert Xu's avatar
Herbert Xu committed
406 407
	int err;

408
	ablkcipher_request_set_tfm(abreq, enc);
Herbert Xu's avatar
Herbert Xu committed
409 410
	ablkcipher_request_set_callback(abreq, aead_request_flags(req),
					crypto_authenc_encrypt_done, req);
411 412 413
	ablkcipher_request_set_crypt(abreq, req->src, dst, cryptlen, req->iv);

	memcpy(iv, req->iv, crypto_aead_ivsize(authenc));
Herbert Xu's avatar
Herbert Xu committed
414 415 416 417 418

	err = crypto_ablkcipher_encrypt(abreq);
	if (err)
		return err;

419 420 421 422 423 424
	return crypto_authenc_genicv(req, iv, CRYPTO_TFM_REQ_MAY_SLEEP);
}

static void crypto_authenc_givencrypt_done(struct crypto_async_request *req,
					   int err)
{
425 426
	struct aead_request *areq = req->data;

427
	if (!err) {
428
		struct skcipher_givcrypt_request *greq = aead_request_ctx(areq);
429

430
		err = crypto_authenc_genicv(areq, greq->giv, 0);
431 432
	}

433
	authenc_request_complete(areq, err);
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
}

static int crypto_authenc_givencrypt(struct aead_givcrypt_request *req)
{
	struct crypto_aead *authenc = aead_givcrypt_reqtfm(req);
	struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
	struct aead_request *areq = &req->areq;
	struct skcipher_givcrypt_request *greq = aead_request_ctx(areq);
	u8 *iv = req->giv;
	int err;

	skcipher_givcrypt_set_tfm(greq, ctx->enc);
	skcipher_givcrypt_set_callback(greq, aead_request_flags(areq),
				       crypto_authenc_givencrypt_done, areq);
	skcipher_givcrypt_set_crypt(greq, areq->src, areq->dst, areq->cryptlen,
				    areq->iv);
	skcipher_givcrypt_set_giv(greq, iv, req->seq);

	err = crypto_skcipher_givencrypt(greq);
	if (err)
		return err;

	return crypto_authenc_genicv(areq, iv, CRYPTO_TFM_REQ_MAY_SLEEP);
Herbert Xu's avatar
Herbert Xu committed
457 458
}

459
static int crypto_authenc_verify(struct aead_request *req,
460
				 authenc_ahash_t authenc_ahash_fn)
Herbert Xu's avatar
Herbert Xu committed
461 462
{
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
463
	struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
464
	u8 *ohash;
Herbert Xu's avatar
Herbert Xu committed
465 466 467
	u8 *ihash;
	unsigned int authsize;

468
	areq_ctx->complete = authenc_verify_ahash_done;
469
	areq_ctx->update_complete = authenc_verify_ahash_update_done;
470 471

	ohash = authenc_ahash_fn(req, CRYPTO_TFM_REQ_MAY_SLEEP);
472 473
	if (IS_ERR(ohash))
		return PTR_ERR(ohash);
Herbert Xu's avatar
Herbert Xu committed
474

475
	authsize = crypto_aead_authsize(authenc);
476
	ihash = ohash + authsize;
477 478
	scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen,
				 authsize, 0);
479
	return memcmp(ihash, ohash, authsize) ? -EBADMSG : 0;
Herbert Xu's avatar
Herbert Xu committed
480 481
}

482 483
static int crypto_authenc_iverify(struct aead_request *req, u8 *iv,
				  unsigned int cryptlen)
Herbert Xu's avatar
Herbert Xu committed
484
{
485
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
486
	struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
487
	struct scatterlist *src = req->src;
488 489 490
	struct scatterlist *assoc = req->assoc;
	struct scatterlist *cipher = areq_ctx->cipher;
	struct scatterlist *asg = areq_ctx->asg;
491
	unsigned int ivsize = crypto_aead_ivsize(authenc);
492 493
	authenc_ahash_t authenc_ahash_fn = crypto_authenc_ahash_fb;
	struct page *srcp;
494 495 496 497 498
	u8 *vsrc;

	srcp = sg_page(src);
	vsrc = PageHighMem(srcp) ? NULL : page_address(srcp) + src->offset;

499 500 501 502 503
	if (ivsize) {
		sg_init_table(cipher, 2);
		sg_set_buf(cipher, iv, ivsize);
		authenc_chain(cipher, src, vsrc == iv + ivsize);
		src = cipher;
504 505 506 507 508 509 510 511 512 513
		cryptlen += ivsize;
	}

	if (sg_is_last(assoc)) {
		authenc_ahash_fn = crypto_authenc_ahash;
		sg_init_table(asg, 2);
		sg_set_page(asg, sg_page(assoc), assoc->length, assoc->offset);
		authenc_chain(asg, src, 0);
		src = asg;
		cryptlen += req->assoclen;
514
	}
515

516 517 518 519
	areq_ctx->cryptlen = cryptlen;
	areq_ctx->sg = src;

	return crypto_authenc_verify(req, authenc_ahash_fn);
Herbert Xu's avatar
Herbert Xu committed
520 521 522 523 524 525 526
}

static int crypto_authenc_decrypt(struct aead_request *req)
{
	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
	struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
	struct ablkcipher_request *abreq = aead_request_ctx(req);
527 528
	unsigned int cryptlen = req->cryptlen;
	unsigned int authsize = crypto_aead_authsize(authenc);
529
	u8 *iv = req->iv;
Herbert Xu's avatar
Herbert Xu committed
530 531
	int err;

532 533 534 535
	if (cryptlen < authsize)
		return -EINVAL;
	cryptlen -= authsize;

536
	err = crypto_authenc_iverify(req, iv, cryptlen);
Herbert Xu's avatar
Herbert Xu committed
537 538 539 540 541
	if (err)
		return err;

	ablkcipher_request_set_tfm(abreq, ctx->enc);
	ablkcipher_request_set_callback(abreq, aead_request_flags(req),
542 543
					req->base.complete, req->base.data);
	ablkcipher_request_set_crypt(abreq, req->src, req->dst, cryptlen, iv);
Herbert Xu's avatar
Herbert Xu committed
544 545 546 547 548 549

	return crypto_ablkcipher_decrypt(abreq);
}

static int crypto_authenc_init_tfm(struct crypto_tfm *tfm)
{
550
	struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
Herbert Xu's avatar
Herbert Xu committed
551 552
	struct authenc_instance_ctx *ictx = crypto_instance_ctx(inst);
	struct crypto_authenc_ctx *ctx = crypto_tfm_ctx(tfm);
553
	struct crypto_ahash *auth;
Herbert Xu's avatar
Herbert Xu committed
554 555 556
	struct crypto_ablkcipher *enc;
	int err;

557
	auth = crypto_spawn_ahash(&ictx->auth);
Herbert Xu's avatar
Herbert Xu committed
558 559 560
	if (IS_ERR(auth))
		return PTR_ERR(auth);

561
	enc = crypto_spawn_skcipher(&ictx->enc);
Herbert Xu's avatar
Herbert Xu committed
562 563
	err = PTR_ERR(enc);
	if (IS_ERR(enc))
564
		goto err_free_ahash;
Herbert Xu's avatar
Herbert Xu committed
565 566 567

	ctx->auth = auth;
	ctx->enc = enc;
568

569 570 571 572 573 574 575 576 577
	ctx->reqoff = ALIGN(2 * crypto_ahash_digestsize(auth) +
			    crypto_ahash_alignmask(auth),
			    crypto_ahash_alignmask(auth) + 1) +
		      crypto_ablkcipher_ivsize(enc);

	tfm->crt_aead.reqsize = sizeof(struct authenc_request_ctx) +
				ctx->reqoff +
				max_t(unsigned int,
				crypto_ahash_reqsize(auth) +
578
				sizeof(struct ahash_request),
579
				sizeof(struct skcipher_givcrypt_request) +
580
				crypto_ablkcipher_reqsize(enc));
Herbert Xu's avatar
Herbert Xu committed
581 582 583

	return 0;

584 585
err_free_ahash:
	crypto_free_ahash(auth);
Herbert Xu's avatar
Herbert Xu committed
586 587 588 589 590 591 592
	return err;
}

static void crypto_authenc_exit_tfm(struct crypto_tfm *tfm)
{
	struct crypto_authenc_ctx *ctx = crypto_tfm_ctx(tfm);

593
	crypto_free_ahash(ctx->auth);
Herbert Xu's avatar
Herbert Xu committed
594 595 596 597 598
	crypto_free_ablkcipher(ctx->enc);
}

static struct crypto_instance *crypto_authenc_alloc(struct rtattr **tb)
{
599
	struct crypto_attr_type *algt;
Herbert Xu's avatar
Herbert Xu committed
600
	struct crypto_instance *inst;
601 602
	struct hash_alg_common *auth;
	struct crypto_alg *auth_base;
Herbert Xu's avatar
Herbert Xu committed
603 604
	struct crypto_alg *enc;
	struct authenc_instance_ctx *ctx;
605
	const char *enc_name;
Herbert Xu's avatar
Herbert Xu committed
606 607
	int err;

608 609 610
	algt = crypto_get_attr_type(tb);
	err = PTR_ERR(algt);
	if (IS_ERR(algt))
Herbert Xu's avatar
Herbert Xu committed
611 612
		return ERR_PTR(err);

613 614 615
	if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)
		return ERR_PTR(-EINVAL);

616 617
	auth = ahash_attr_alg(tb[1], CRYPTO_ALG_TYPE_HASH,
			       CRYPTO_ALG_TYPE_AHASH_MASK);
Herbert Xu's avatar
Herbert Xu committed
618
	if (IS_ERR(auth))
Julia Lawall's avatar
Julia Lawall committed
619
		return ERR_CAST(auth);
Herbert Xu's avatar
Herbert Xu committed
620

621 622
	auth_base = &auth->base;

623 624 625
	enc_name = crypto_attr_alg_name(tb[2]);
	err = PTR_ERR(enc_name);
	if (IS_ERR(enc_name))
Herbert Xu's avatar
Herbert Xu committed
626 627 628 629 630
		goto out_put_auth;

	inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
	err = -ENOMEM;
	if (!inst)
631
		goto out_put_auth;
Herbert Xu's avatar
Herbert Xu committed
632 633 634

	ctx = crypto_instance_ctx(inst);

635
	err = crypto_init_ahash_spawn(&ctx->auth, auth, inst);
Herbert Xu's avatar
Herbert Xu committed
636 637 638
	if (err)
		goto err_free_inst;

639 640 641 642
	crypto_set_skcipher_spawn(&ctx->enc, inst);
	err = crypto_grab_skcipher(&ctx->enc, enc_name, 0,
				   crypto_requires_sync(algt->type,
							algt->mask));
Herbert Xu's avatar
Herbert Xu committed
643 644 645
	if (err)
		goto err_drop_auth;

646 647 648 649
	enc = crypto_skcipher_spawn_alg(&ctx->enc);

	err = -ENAMETOOLONG;
	if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME,
650
		     "authenc(%s,%s)", auth_base->cra_name, enc->cra_name) >=
651 652 653 654
	    CRYPTO_MAX_ALG_NAME)
		goto err_drop_enc;

	if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
655
		     "authenc(%s,%s)", auth_base->cra_driver_name,
656 657 658 659 660
		     enc->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
		goto err_drop_enc;

	inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD;
	inst->alg.cra_flags |= enc->cra_flags & CRYPTO_ALG_ASYNC;
661 662
	inst->alg.cra_priority = enc->cra_priority *
				 10 + auth_base->cra_priority;
Herbert Xu's avatar
Herbert Xu committed
663
	inst->alg.cra_blocksize = enc->cra_blocksize;
664
	inst->alg.cra_alignmask = auth_base->cra_alignmask | enc->cra_alignmask;
Herbert Xu's avatar
Herbert Xu committed
665 666
	inst->alg.cra_type = &crypto_aead_type;

667
	inst->alg.cra_aead.ivsize = enc->cra_ablkcipher.ivsize;
668
	inst->alg.cra_aead.maxauthsize = auth->digestsize;
Herbert Xu's avatar
Herbert Xu committed
669 670 671 672 673 674 675 676 677

	inst->alg.cra_ctxsize = sizeof(struct crypto_authenc_ctx);

	inst->alg.cra_init = crypto_authenc_init_tfm;
	inst->alg.cra_exit = crypto_authenc_exit_tfm;

	inst->alg.cra_aead.setkey = crypto_authenc_setkey;
	inst->alg.cra_aead.encrypt = crypto_authenc_encrypt;
	inst->alg.cra_aead.decrypt = crypto_authenc_decrypt;
678
	inst->alg.cra_aead.givencrypt = crypto_authenc_givencrypt;
Herbert Xu's avatar
Herbert Xu committed
679 680

out:
681
	crypto_mod_put(auth_base);
Herbert Xu's avatar
Herbert Xu committed
682 683
	return inst;

684 685
err_drop_enc:
	crypto_drop_skcipher(&ctx->enc);
Herbert Xu's avatar
Herbert Xu committed
686
err_drop_auth:
687
	crypto_drop_ahash(&ctx->auth);
Herbert Xu's avatar
Herbert Xu committed
688 689
err_free_inst:
	kfree(inst);
690
out_put_auth:
Herbert Xu's avatar
Herbert Xu committed
691 692 693 694 695 696 697 698
	inst = ERR_PTR(err);
	goto out;
}

static void crypto_authenc_free(struct crypto_instance *inst)
{
	struct authenc_instance_ctx *ctx = crypto_instance_ctx(inst);

699
	crypto_drop_skcipher(&ctx->enc);
700
	crypto_drop_ahash(&ctx->auth);
Herbert Xu's avatar
Herbert Xu committed
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
	kfree(inst);
}

static struct crypto_template crypto_authenc_tmpl = {
	.name = "authenc",
	.alloc = crypto_authenc_alloc,
	.free = crypto_authenc_free,
	.module = THIS_MODULE,
};

static int __init crypto_authenc_module_init(void)
{
	return crypto_register_template(&crypto_authenc_tmpl);
}

static void __exit crypto_authenc_module_exit(void)
{
	crypto_unregister_template(&crypto_authenc_tmpl);
}

module_init(crypto_authenc_module_init);
module_exit(crypto_authenc_module_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple AEAD wrapper for IPsec");