1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * AEAD wrapper for Kerberos 5 RFC3961 simplified profile.
4 *
5 * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
6 * Written by David Howells (dhowells@redhat.com)
7 *
8 * Derived from authenc:
9 * Copyright (c) 2007-2015 Herbert Xu <herbert@gondor.apana.org.au>
10 */
11
12 #include <crypto/internal/aead.h>
13 #include <crypto/internal/hash.h>
14 #include <crypto/internal/skcipher.h>
15 #include <crypto/authenc.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
25 struct krb5enc_instance_ctx {
26 struct crypto_ahash_spawn auth;
27 struct crypto_skcipher_spawn enc;
28 unsigned int reqoff;
29 };
30
31 struct krb5enc_ctx {
32 struct crypto_ahash *auth;
33 struct crypto_skcipher *enc;
34 };
35
36 struct krb5enc_request_ctx {
37 struct scatterlist src[2];
38 struct scatterlist dst[2];
39 char tail[];
40 };
41
krb5enc_request_complete(struct aead_request * req,int err)42 static void krb5enc_request_complete(struct aead_request *req, int err)
43 {
44 if (err != -EINPROGRESS)
45 aead_request_complete(req, err);
46 }
47
48 /**
49 * crypto_krb5enc_extractkeys - Extract Ke and Ki keys from the key blob.
50 * @keys: Where to put the key sizes and pointers
51 * @key: Encoded key material
52 * @keylen: Amount of key material
53 *
54 * Decode the key blob we're given. It starts with an rtattr that indicates
55 * the format and the length. Format CRYPTO_AUTHENC_KEYA_PARAM is:
56 *
57 * rtattr || __be32 enckeylen || authkey || enckey
58 *
59 * Note that the rtattr is in cpu-endian form, unlike enckeylen. This must be
60 * handled correctly in static testmgr data.
61 */
crypto_krb5enc_extractkeys(struct crypto_authenc_keys * keys,const u8 * key,unsigned int keylen)62 int crypto_krb5enc_extractkeys(struct crypto_authenc_keys *keys, const u8 *key,
63 unsigned int keylen)
64 {
65 struct rtattr *rta = (struct rtattr *)key;
66 struct crypto_authenc_key_param *param;
67
68 if (!RTA_OK(rta, keylen))
69 return -EINVAL;
70 if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
71 return -EINVAL;
72
73 /*
74 * RTA_OK() didn't align the rtattr's payload when validating that it
75 * fits in the buffer. Yet, the keys should start on the next 4-byte
76 * aligned boundary. To avoid confusion, require that the rtattr
77 * payload be exactly the param struct, which has a 4-byte aligned size.
78 */
79 if (RTA_PAYLOAD(rta) != sizeof(*param))
80 return -EINVAL;
81 BUILD_BUG_ON(sizeof(*param) % RTA_ALIGNTO);
82
83 param = RTA_DATA(rta);
84 keys->enckeylen = be32_to_cpu(param->enckeylen);
85
86 key += rta->rta_len;
87 keylen -= rta->rta_len;
88
89 if (keylen < keys->enckeylen)
90 return -EINVAL;
91
92 keys->authkeylen = keylen - keys->enckeylen;
93 keys->authkey = key;
94 keys->enckey = key + keys->authkeylen;
95 return 0;
96 }
97 EXPORT_SYMBOL(crypto_krb5enc_extractkeys);
98
krb5enc_setkey(struct crypto_aead * krb5enc,const u8 * key,unsigned int keylen)99 static int krb5enc_setkey(struct crypto_aead *krb5enc, const u8 *key,
100 unsigned int keylen)
101 {
102 struct crypto_authenc_keys keys;
103 struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
104 struct crypto_skcipher *enc = ctx->enc;
105 struct crypto_ahash *auth = ctx->auth;
106 unsigned int flags = crypto_aead_get_flags(krb5enc);
107 int err = -EINVAL;
108
109 if (crypto_krb5enc_extractkeys(&keys, key, keylen) != 0)
110 goto out;
111
112 crypto_ahash_clear_flags(auth, CRYPTO_TFM_REQ_MASK);
113 crypto_ahash_set_flags(auth, flags & CRYPTO_TFM_REQ_MASK);
114 err = crypto_ahash_setkey(auth, keys.authkey, keys.authkeylen);
115 if (err)
116 goto out;
117
118 crypto_skcipher_clear_flags(enc, CRYPTO_TFM_REQ_MASK);
119 crypto_skcipher_set_flags(enc, flags & CRYPTO_TFM_REQ_MASK);
120 err = crypto_skcipher_setkey(enc, keys.enckey, keys.enckeylen);
121 out:
122 memzero_explicit(&keys, sizeof(keys));
123 return err;
124 }
125
krb5enc_encrypt_done(void * data,int err)126 static void krb5enc_encrypt_done(void *data, int err)
127 {
128 struct aead_request *req = data;
129
130 krb5enc_request_complete(req, err);
131 }
132
133 /*
134 * Start the encryption of the plaintext. We skip over the associated data as
135 * that only gets included in the hash.
136 */
krb5enc_dispatch_encrypt(struct aead_request * req,unsigned int flags)137 static int krb5enc_dispatch_encrypt(struct aead_request *req,
138 unsigned int flags)
139 {
140 struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
141 struct aead_instance *inst = aead_alg_instance(krb5enc);
142 struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
143 struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
144 struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
145 struct crypto_skcipher *enc = ctx->enc;
146 struct skcipher_request *skreq = (void *)(areq_ctx->tail +
147 ictx->reqoff);
148 struct scatterlist *src, *dst;
149
150 src = scatterwalk_ffwd(areq_ctx->src, req->src, req->assoclen);
151 if (req->src == req->dst)
152 dst = src;
153 else
154 dst = scatterwalk_ffwd(areq_ctx->dst, req->dst, req->assoclen);
155
156 skcipher_request_set_tfm(skreq, enc);
157 skcipher_request_set_callback(skreq, aead_request_flags(req),
158 krb5enc_encrypt_done, req);
159 skcipher_request_set_crypt(skreq, src, dst, req->cryptlen, req->iv);
160
161 return crypto_skcipher_encrypt(skreq);
162 }
163
164 /*
165 * Insert the hash into the checksum field in the destination buffer directly
166 * after the encrypted region.
167 */
krb5enc_insert_checksum(struct aead_request * req,u8 * hash)168 static void krb5enc_insert_checksum(struct aead_request *req, u8 *hash)
169 {
170 struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
171
172 scatterwalk_map_and_copy(hash, req->dst,
173 req->assoclen + req->cryptlen,
174 crypto_aead_authsize(krb5enc), 1);
175 }
176
177 /*
178 * Upon completion of an asynchronous digest, transfer the hash to the checksum
179 * field.
180 */
krb5enc_encrypt_ahash_done(void * data,int err)181 static void krb5enc_encrypt_ahash_done(void *data, int err)
182 {
183 struct aead_request *req = data;
184 struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
185 struct aead_instance *inst = aead_alg_instance(krb5enc);
186 struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
187 struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
188 struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
189
190 if (err)
191 return krb5enc_request_complete(req, err);
192
193 krb5enc_insert_checksum(req, ahreq->result);
194
195 err = krb5enc_dispatch_encrypt(req, 0);
196 if (err != -EINPROGRESS)
197 aead_request_complete(req, err);
198 }
199
200 /*
201 * Start the digest of the plaintext for encryption. In theory, this could be
202 * run in parallel with the encryption, provided the src and dst buffers don't
203 * overlap.
204 */
krb5enc_dispatch_encrypt_hash(struct aead_request * req)205 static int krb5enc_dispatch_encrypt_hash(struct aead_request *req)
206 {
207 struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
208 struct aead_instance *inst = aead_alg_instance(krb5enc);
209 struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
210 struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
211 struct crypto_ahash *auth = ctx->auth;
212 struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
213 struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
214 u8 *hash = areq_ctx->tail;
215 int err;
216
217 ahash_request_set_callback(ahreq, aead_request_flags(req),
218 krb5enc_encrypt_ahash_done, req);
219 ahash_request_set_tfm(ahreq, auth);
220 ahash_request_set_crypt(ahreq, req->src, hash, req->assoclen + req->cryptlen);
221
222 err = crypto_ahash_digest(ahreq);
223 if (err)
224 return err;
225
226 krb5enc_insert_checksum(req, hash);
227 return 0;
228 }
229
230 /*
231 * Process an encryption operation. We can perform the cipher and the hash in
232 * parallel, provided the src and dst buffers are separate.
233 */
krb5enc_encrypt(struct aead_request * req)234 static int krb5enc_encrypt(struct aead_request *req)
235 {
236 int err;
237
238 err = krb5enc_dispatch_encrypt_hash(req);
239 if (err < 0)
240 return err;
241
242 return krb5enc_dispatch_encrypt(req, aead_request_flags(req));
243 }
244
krb5enc_verify_hash(struct aead_request * req)245 static int krb5enc_verify_hash(struct aead_request *req)
246 {
247 struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
248 struct aead_instance *inst = aead_alg_instance(krb5enc);
249 struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
250 struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
251 struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
252 unsigned int authsize = crypto_aead_authsize(krb5enc);
253 u8 *calc_hash = areq_ctx->tail;
254 u8 *msg_hash = areq_ctx->tail + authsize;
255
256 scatterwalk_map_and_copy(msg_hash, req->src, ahreq->nbytes, authsize, 0);
257
258 if (crypto_memneq(msg_hash, calc_hash, authsize))
259 return -EBADMSG;
260 return 0;
261 }
262
krb5enc_decrypt_hash_done(void * data,int err)263 static void krb5enc_decrypt_hash_done(void *data, int err)
264 {
265 struct aead_request *req = data;
266
267 if (err)
268 return krb5enc_request_complete(req, err);
269
270 err = krb5enc_verify_hash(req);
271 krb5enc_request_complete(req, err);
272 }
273
274 /*
275 * Dispatch the hashing of the plaintext after we've done the decryption.
276 */
krb5enc_dispatch_decrypt_hash(struct aead_request * req)277 static int krb5enc_dispatch_decrypt_hash(struct aead_request *req)
278 {
279 struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
280 struct aead_instance *inst = aead_alg_instance(krb5enc);
281 struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
282 struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
283 struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
284 struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
285 struct crypto_ahash *auth = ctx->auth;
286 unsigned int authsize = crypto_aead_authsize(krb5enc);
287 u8 *hash = areq_ctx->tail;
288 int err;
289
290 ahash_request_set_tfm(ahreq, auth);
291 ahash_request_set_crypt(ahreq, req->dst, hash,
292 req->assoclen + req->cryptlen - authsize);
293 ahash_request_set_callback(ahreq, aead_request_flags(req),
294 krb5enc_decrypt_hash_done, req);
295
296 err = crypto_ahash_digest(ahreq);
297 if (err < 0)
298 return err;
299
300 return krb5enc_verify_hash(req);
301 }
302
303 /*
304 * Dispatch the decryption of the ciphertext.
305 */
krb5enc_dispatch_decrypt(struct aead_request * req)306 static int krb5enc_dispatch_decrypt(struct aead_request *req)
307 {
308 struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
309 struct aead_instance *inst = aead_alg_instance(krb5enc);
310 struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
311 struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
312 struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
313 struct skcipher_request *skreq = (void *)(areq_ctx->tail +
314 ictx->reqoff);
315 unsigned int authsize = crypto_aead_authsize(krb5enc);
316 struct scatterlist *src, *dst;
317
318 src = scatterwalk_ffwd(areq_ctx->src, req->src, req->assoclen);
319 dst = src;
320
321 if (req->src != req->dst)
322 dst = scatterwalk_ffwd(areq_ctx->dst, req->dst, req->assoclen);
323
324 skcipher_request_set_tfm(skreq, ctx->enc);
325 skcipher_request_set_callback(skreq, aead_request_flags(req),
326 req->base.complete, req->base.data);
327 skcipher_request_set_crypt(skreq, src, dst,
328 req->cryptlen - authsize, req->iv);
329
330 return crypto_skcipher_decrypt(skreq);
331 }
332
krb5enc_decrypt(struct aead_request * req)333 static int krb5enc_decrypt(struct aead_request *req)
334 {
335 int err;
336
337 err = krb5enc_dispatch_decrypt(req);
338 if (err < 0)
339 return err;
340
341 return krb5enc_dispatch_decrypt_hash(req);
342 }
343
krb5enc_init_tfm(struct crypto_aead * tfm)344 static int krb5enc_init_tfm(struct crypto_aead *tfm)
345 {
346 struct aead_instance *inst = aead_alg_instance(tfm);
347 struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
348 struct krb5enc_ctx *ctx = crypto_aead_ctx(tfm);
349 struct crypto_ahash *auth;
350 struct crypto_skcipher *enc;
351 int err;
352
353 auth = crypto_spawn_ahash(&ictx->auth);
354 if (IS_ERR(auth))
355 return PTR_ERR(auth);
356
357 enc = crypto_spawn_skcipher(&ictx->enc);
358 err = PTR_ERR(enc);
359 if (IS_ERR(enc))
360 goto err_free_ahash;
361
362 ctx->auth = auth;
363 ctx->enc = enc;
364
365 crypto_aead_set_reqsize(
366 tfm,
367 sizeof(struct krb5enc_request_ctx) +
368 ictx->reqoff + /* Space for two checksums */
369 umax(sizeof(struct ahash_request) + crypto_ahash_reqsize(auth),
370 sizeof(struct skcipher_request) + crypto_skcipher_reqsize(enc)));
371
372 return 0;
373
374 err_free_ahash:
375 crypto_free_ahash(auth);
376 return err;
377 }
378
krb5enc_exit_tfm(struct crypto_aead * tfm)379 static void krb5enc_exit_tfm(struct crypto_aead *tfm)
380 {
381 struct krb5enc_ctx *ctx = crypto_aead_ctx(tfm);
382
383 crypto_free_ahash(ctx->auth);
384 crypto_free_skcipher(ctx->enc);
385 }
386
krb5enc_free(struct aead_instance * inst)387 static void krb5enc_free(struct aead_instance *inst)
388 {
389 struct krb5enc_instance_ctx *ctx = aead_instance_ctx(inst);
390
391 crypto_drop_skcipher(&ctx->enc);
392 crypto_drop_ahash(&ctx->auth);
393 kfree(inst);
394 }
395
396 /*
397 * Create an instance of a template for a specific hash and cipher pair.
398 */
krb5enc_create(struct crypto_template * tmpl,struct rtattr ** tb)399 static int krb5enc_create(struct crypto_template *tmpl, struct rtattr **tb)
400 {
401 struct krb5enc_instance_ctx *ictx;
402 struct skcipher_alg_common *enc;
403 struct hash_alg_common *auth;
404 struct aead_instance *inst;
405 struct crypto_alg *auth_base;
406 u32 mask;
407 int err;
408
409 err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_AEAD, &mask);
410 if (err) {
411 pr_err("attr_type failed\n");
412 return err;
413 }
414
415 inst = kzalloc(sizeof(*inst) + sizeof(*ictx), GFP_KERNEL);
416 if (!inst)
417 return -ENOMEM;
418 ictx = aead_instance_ctx(inst);
419
420 err = crypto_grab_ahash(&ictx->auth, aead_crypto_instance(inst),
421 crypto_attr_alg_name(tb[1]), 0, mask);
422 if (err) {
423 pr_err("grab ahash failed\n");
424 goto err_free_inst;
425 }
426 auth = crypto_spawn_ahash_alg(&ictx->auth);
427 auth_base = &auth->base;
428
429 err = crypto_grab_skcipher(&ictx->enc, aead_crypto_instance(inst),
430 crypto_attr_alg_name(tb[2]), 0, mask);
431 if (err) {
432 pr_err("grab skcipher failed\n");
433 goto err_free_inst;
434 }
435 enc = crypto_spawn_skcipher_alg_common(&ictx->enc);
436
437 ictx->reqoff = 2 * auth->digestsize;
438
439 err = -ENAMETOOLONG;
440 if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
441 "krb5enc(%s,%s)", auth_base->cra_name,
442 enc->base.cra_name) >=
443 CRYPTO_MAX_ALG_NAME)
444 goto err_free_inst;
445
446 if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
447 "krb5enc(%s,%s)", auth_base->cra_driver_name,
448 enc->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
449 goto err_free_inst;
450
451 inst->alg.base.cra_priority = enc->base.cra_priority * 10 +
452 auth_base->cra_priority;
453 inst->alg.base.cra_blocksize = enc->base.cra_blocksize;
454 inst->alg.base.cra_alignmask = enc->base.cra_alignmask;
455 inst->alg.base.cra_ctxsize = sizeof(struct krb5enc_ctx);
456
457 inst->alg.ivsize = enc->ivsize;
458 inst->alg.chunksize = enc->chunksize;
459 inst->alg.maxauthsize = auth->digestsize;
460
461 inst->alg.init = krb5enc_init_tfm;
462 inst->alg.exit = krb5enc_exit_tfm;
463
464 inst->alg.setkey = krb5enc_setkey;
465 inst->alg.encrypt = krb5enc_encrypt;
466 inst->alg.decrypt = krb5enc_decrypt;
467
468 inst->free = krb5enc_free;
469
470 err = aead_register_instance(tmpl, inst);
471 if (err) {
472 pr_err("ref failed\n");
473 goto err_free_inst;
474 }
475
476 return 0;
477
478 err_free_inst:
479 krb5enc_free(inst);
480 return err;
481 }
482
483 static struct crypto_template crypto_krb5enc_tmpl = {
484 .name = "krb5enc",
485 .create = krb5enc_create,
486 .module = THIS_MODULE,
487 };
488
crypto_krb5enc_module_init(void)489 static int __init crypto_krb5enc_module_init(void)
490 {
491 return crypto_register_template(&crypto_krb5enc_tmpl);
492 }
493
crypto_krb5enc_module_exit(void)494 static void __exit crypto_krb5enc_module_exit(void)
495 {
496 crypto_unregister_template(&crypto_krb5enc_tmpl);
497 }
498
499 subsys_initcall(crypto_krb5enc_module_init);
500 module_exit(crypto_krb5enc_module_exit);
501
502 MODULE_LICENSE("GPL");
503 MODULE_DESCRIPTION("Simple AEAD wrapper for Kerberos 5 RFC3961");
504 MODULE_ALIAS_CRYPTO("krb5enc");
505