|  | // SPDX-License-Identifier: BSD-3-Clause | 
|  | /* rfc3961 Kerberos 5 simplified crypto profile. | 
|  | * | 
|  | * Parts borrowed from net/sunrpc/auth_gss/. | 
|  | */ | 
|  | /* | 
|  | * COPYRIGHT (c) 2008 | 
|  | * The Regents of the University of Michigan | 
|  | * ALL RIGHTS RESERVED | 
|  | * | 
|  | * Permission is granted to use, copy, create derivative works | 
|  | * and redistribute this software and such derivative works | 
|  | * for any purpose, so long as the name of The University of | 
|  | * Michigan is not used in any advertising or publicity | 
|  | * pertaining to the use of distribution of this software | 
|  | * without specific, written prior authorization.  If the | 
|  | * above copyright notice or any other identification of the | 
|  | * University of Michigan is included in any copy of any | 
|  | * portion of this software, then the disclaimer below must | 
|  | * also be included. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION | 
|  | * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY | 
|  | * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF | 
|  | * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING | 
|  | * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF | 
|  | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE | 
|  | * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE | 
|  | * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR | 
|  | * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING | 
|  | * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN | 
|  | * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF | 
|  | * SUCH DAMAGES. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Copyright (C) 1998 by the FundsXpress, INC. | 
|  | * | 
|  | * All rights reserved. | 
|  | * | 
|  | * Export of this software from the United States of America may require | 
|  | * a specific license from the United States Government.  It is the | 
|  | * responsibility of any person or organization contemplating export to | 
|  | * obtain such a license before exporting. | 
|  | * | 
|  | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and | 
|  | * distribute this software and its documentation for any purpose and | 
|  | * without fee is hereby granted, provided that the above copyright | 
|  | * notice appear in all copies and that both that copyright notice and | 
|  | * this permission notice appear in supporting documentation, and that | 
|  | * the name of FundsXpress. not be used in advertising or publicity pertaining | 
|  | * to distribution of the software without specific, written prior | 
|  | * permission.  FundsXpress makes no representations about the suitability of | 
|  | * this software for any purpose.  It is provided "as is" without express | 
|  | * or implied warranty. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | 
|  | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | 
|  | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. | 
|  | * Written by David Howells (dhowells@redhat.com) | 
|  | */ | 
|  |  | 
|  | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|  |  | 
|  | #include <linux/random.h> | 
|  | #include <linux/scatterlist.h> | 
|  | #include <linux/skbuff.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/lcm.h> | 
|  | #include <linux/rtnetlink.h> | 
|  | #include <crypto/authenc.h> | 
|  | #include <crypto/skcipher.h> | 
|  | #include <crypto/hash.h> | 
|  | #include "internal.h" | 
|  |  | 
|  | /* Maximum blocksize for the supported crypto algorithms */ | 
|  | #define KRB5_MAX_BLOCKSIZE  (16) | 
|  |  | 
|  | int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg, | 
|  | size_t offset, size_t len) | 
|  | { | 
|  | struct sg_mapping_iter miter; | 
|  | size_t i, n; | 
|  | int ret = 0; | 
|  |  | 
|  | sg_miter_start(&miter, sg, sg_nents(sg), | 
|  | SG_MITER_FROM_SG | SG_MITER_LOCAL); | 
|  | sg_miter_skip(&miter, offset); | 
|  | for (i = 0; i < len; i += n) { | 
|  | sg_miter_next(&miter); | 
|  | n = min(miter.length, len - i); | 
|  | ret = crypto_shash_update(desc, miter.addr, n); | 
|  | if (ret < 0) | 
|  | break; | 
|  | } | 
|  | sg_miter_stop(&miter); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int rfc3961_do_encrypt(struct crypto_sync_skcipher *tfm, void *iv, | 
|  | const struct krb5_buffer *in, struct krb5_buffer *out) | 
|  | { | 
|  | struct scatterlist sg[1]; | 
|  | u8 local_iv[KRB5_MAX_BLOCKSIZE] __aligned(KRB5_MAX_BLOCKSIZE) = {0}; | 
|  | SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm); | 
|  | int ret; | 
|  |  | 
|  | if (WARN_ON(in->len != out->len)) | 
|  | return -EINVAL; | 
|  | if (out->len % crypto_sync_skcipher_blocksize(tfm) != 0) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (crypto_sync_skcipher_ivsize(tfm) > KRB5_MAX_BLOCKSIZE) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (iv) | 
|  | memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm)); | 
|  |  | 
|  | memcpy(out->data, in->data, out->len); | 
|  | sg_init_one(sg, out->data, out->len); | 
|  |  | 
|  | skcipher_request_set_sync_tfm(req, tfm); | 
|  | skcipher_request_set_callback(req, 0, NULL, NULL); | 
|  | skcipher_request_set_crypt(req, sg, sg, out->len, local_iv); | 
|  |  | 
|  | ret = crypto_skcipher_encrypt(req); | 
|  | skcipher_request_zero(req); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Calculate an unkeyed basic hash. | 
|  | */ | 
|  | static int rfc3961_calc_H(const struct krb5_enctype *krb5, | 
|  | const struct krb5_buffer *data, | 
|  | struct krb5_buffer *digest, | 
|  | gfp_t gfp) | 
|  | { | 
|  | struct crypto_shash *tfm; | 
|  | struct shash_desc *desc; | 
|  | size_t desc_size; | 
|  | int ret = -ENOMEM; | 
|  |  | 
|  | tfm = crypto_alloc_shash(krb5->hash_name, 0, 0); | 
|  | if (IS_ERR(tfm)) | 
|  | return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); | 
|  |  | 
|  | desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); | 
|  |  | 
|  | desc = kzalloc(desc_size, gfp); | 
|  | if (!desc) | 
|  | goto error_tfm; | 
|  |  | 
|  | digest->len = crypto_shash_digestsize(tfm); | 
|  | digest->data = kzalloc(digest->len, gfp); | 
|  | if (!digest->data) | 
|  | goto error_desc; | 
|  |  | 
|  | desc->tfm = tfm; | 
|  | ret = crypto_shash_init(desc); | 
|  | if (ret < 0) | 
|  | goto error_digest; | 
|  |  | 
|  | ret = crypto_shash_finup(desc, data->data, data->len, digest->data); | 
|  | if (ret < 0) | 
|  | goto error_digest; | 
|  |  | 
|  | goto error_desc; | 
|  |  | 
|  | error_digest: | 
|  | kfree_sensitive(digest->data); | 
|  | error_desc: | 
|  | kfree_sensitive(desc); | 
|  | error_tfm: | 
|  | crypto_free_shash(tfm); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This is the n-fold function as described in rfc3961, sec 5.1 | 
|  | * Taken from MIT Kerberos and modified. | 
|  | */ | 
|  | static void rfc3961_nfold(const struct krb5_buffer *source, struct krb5_buffer *result) | 
|  | { | 
|  | const u8 *in = source->data; | 
|  | u8 *out = result->data; | 
|  | unsigned long ulcm; | 
|  | unsigned int inbits, outbits; | 
|  | int byte, i, msbit; | 
|  |  | 
|  | /* the code below is more readable if I make these bytes instead of bits */ | 
|  | inbits = source->len; | 
|  | outbits = result->len; | 
|  |  | 
|  | /* first compute lcm(n,k) */ | 
|  | ulcm = lcm(inbits, outbits); | 
|  |  | 
|  | /* now do the real work */ | 
|  | memset(out, 0, outbits); | 
|  | byte = 0; | 
|  |  | 
|  | /* this will end up cycling through k lcm(k,n)/k times, which | 
|  | * is correct. | 
|  | */ | 
|  | for (i = ulcm-1; i >= 0; i--) { | 
|  | /* compute the msbit in k which gets added into this byte */ | 
|  | msbit = ( | 
|  | /* first, start with the msbit in the first, | 
|  | * unrotated byte | 
|  | */ | 
|  | ((inbits << 3) - 1) + | 
|  | /* then, for each byte, shift to the right | 
|  | * for each repetition | 
|  | */ | 
|  | (((inbits << 3) + 13) * (i/inbits)) + | 
|  | /* last, pick out the correct byte within | 
|  | * that shifted repetition | 
|  | */ | 
|  | ((inbits - (i % inbits)) << 3) | 
|  | ) % (inbits << 3); | 
|  |  | 
|  | /* pull out the byte value itself */ | 
|  | byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8) | | 
|  | (in[((inbits)     - (msbit >> 3)) % inbits])) | 
|  | >> ((msbit & 7) + 1)) & 0xff; | 
|  |  | 
|  | /* do the addition */ | 
|  | byte += out[i % outbits]; | 
|  | out[i % outbits] = byte & 0xff; | 
|  |  | 
|  | /* keep around the carry bit, if any */ | 
|  | byte >>= 8; | 
|  | } | 
|  |  | 
|  | /* if there's a carry bit left over, add it back in */ | 
|  | if (byte) { | 
|  | for (i = outbits - 1; i >= 0; i--) { | 
|  | /* do the addition */ | 
|  | byte += out[i]; | 
|  | out[i] = byte & 0xff; | 
|  |  | 
|  | /* keep around the carry bit, if any */ | 
|  | byte >>= 8; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Calculate a derived key, DK(Base Key, Well-Known Constant) | 
|  | * | 
|  | * DK(Key, Constant) = random-to-key(DR(Key, Constant)) | 
|  | * DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state)) | 
|  | * K1 = E(Key, n-fold(Constant), initial-cipher-state) | 
|  | * K2 = E(Key, K1, initial-cipher-state) | 
|  | * K3 = E(Key, K2, initial-cipher-state) | 
|  | * K4 = ... | 
|  | * DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...) | 
|  | * [rfc3961 sec 5.1] | 
|  | */ | 
|  | static int rfc3961_calc_DK(const struct krb5_enctype *krb5, | 
|  | const struct krb5_buffer *inkey, | 
|  | const struct krb5_buffer *in_constant, | 
|  | struct krb5_buffer *result, | 
|  | gfp_t gfp) | 
|  | { | 
|  | unsigned int blocksize, keybytes, keylength, n; | 
|  | struct krb5_buffer inblock, outblock, rawkey; | 
|  | struct crypto_sync_skcipher *cipher; | 
|  | int ret = -EINVAL; | 
|  |  | 
|  | blocksize = krb5->block_len; | 
|  | keybytes = krb5->key_bytes; | 
|  | keylength = krb5->key_len; | 
|  |  | 
|  | if (inkey->len != keylength || result->len != keylength) | 
|  | return -EINVAL; | 
|  | if (!krb5->random_to_key && result->len != keybytes) | 
|  | return -EINVAL; | 
|  |  | 
|  | cipher = crypto_alloc_sync_skcipher(krb5->derivation_enc, 0, 0); | 
|  | if (IS_ERR(cipher)) { | 
|  | ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher); | 
|  | goto err_return; | 
|  | } | 
|  | ret = crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len); | 
|  | if (ret < 0) | 
|  | goto err_free_cipher; | 
|  |  | 
|  | ret = -ENOMEM; | 
|  | inblock.data = kzalloc(blocksize * 2 + keybytes, gfp); | 
|  | if (!inblock.data) | 
|  | goto err_free_cipher; | 
|  |  | 
|  | inblock.len	= blocksize; | 
|  | outblock.data	= inblock.data + blocksize; | 
|  | outblock.len	= blocksize; | 
|  | rawkey.data	= outblock.data + blocksize; | 
|  | rawkey.len	= keybytes; | 
|  |  | 
|  | /* initialize the input block */ | 
|  |  | 
|  | if (in_constant->len == inblock.len) | 
|  | memcpy(inblock.data, in_constant->data, inblock.len); | 
|  | else | 
|  | rfc3961_nfold(in_constant, &inblock); | 
|  |  | 
|  | /* loop encrypting the blocks until enough key bytes are generated */ | 
|  | n = 0; | 
|  | while (n < rawkey.len) { | 
|  | rfc3961_do_encrypt(cipher, NULL, &inblock, &outblock); | 
|  |  | 
|  | if (keybytes - n <= outblock.len) { | 
|  | memcpy(rawkey.data + n, outblock.data, keybytes - n); | 
|  | break; | 
|  | } | 
|  |  | 
|  | memcpy(rawkey.data + n, outblock.data, outblock.len); | 
|  | memcpy(inblock.data, outblock.data, outblock.len); | 
|  | n += outblock.len; | 
|  | } | 
|  |  | 
|  | /* postprocess the key */ | 
|  | if (!krb5->random_to_key) { | 
|  | /* Identity random-to-key function. */ | 
|  | memcpy(result->data, rawkey.data, rawkey.len); | 
|  | ret = 0; | 
|  | } else { | 
|  | ret = krb5->random_to_key(krb5, &rawkey, result); | 
|  | } | 
|  |  | 
|  | kfree_sensitive(inblock.data); | 
|  | err_free_cipher: | 
|  | crypto_free_sync_skcipher(cipher); | 
|  | err_return: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Calculate single encryption, E() | 
|  | * | 
|  | *	E(Key, octets) | 
|  | */ | 
|  | static int rfc3961_calc_E(const struct krb5_enctype *krb5, | 
|  | const struct krb5_buffer *key, | 
|  | const struct krb5_buffer *in_data, | 
|  | struct krb5_buffer *result, | 
|  | gfp_t gfp) | 
|  | { | 
|  | struct crypto_sync_skcipher *cipher; | 
|  | int ret; | 
|  |  | 
|  | cipher = crypto_alloc_sync_skcipher(krb5->derivation_enc, 0, 0); | 
|  | if (IS_ERR(cipher)) { | 
|  | ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher); | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | ret = crypto_sync_skcipher_setkey(cipher, key->data, key->len); | 
|  | if (ret < 0) | 
|  | goto err_free; | 
|  |  | 
|  | ret = rfc3961_do_encrypt(cipher, NULL, in_data, result); | 
|  |  | 
|  | err_free: | 
|  | crypto_free_sync_skcipher(cipher); | 
|  | err: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Calculate the pseudo-random function, PRF(). | 
|  | * | 
|  | *      tmp1 = H(octet-string) | 
|  | *      tmp2 = truncate tmp1 to multiple of m | 
|  | *      PRF = E(DK(protocol-key, prfconstant), tmp2, initial-cipher-state) | 
|  | * | 
|  | *      The "prfconstant" used in the PRF operation is the three-octet string | 
|  | *      "prf". | 
|  | *      [rfc3961 sec 5.3] | 
|  | */ | 
|  | static int rfc3961_calc_PRF(const struct krb5_enctype *krb5, | 
|  | const struct krb5_buffer *protocol_key, | 
|  | const struct krb5_buffer *octet_string, | 
|  | struct krb5_buffer *result, | 
|  | gfp_t gfp) | 
|  | { | 
|  | static const struct krb5_buffer prfconstant = { 3, "prf" }; | 
|  | struct krb5_buffer derived_key; | 
|  | struct krb5_buffer tmp1, tmp2; | 
|  | unsigned int m = krb5->block_len; | 
|  | void *buffer; | 
|  | int ret; | 
|  |  | 
|  | if (result->len != krb5->prf_len) | 
|  | return -EINVAL; | 
|  |  | 
|  | tmp1.len = krb5->hash_len; | 
|  | derived_key.len = krb5->key_bytes; | 
|  | buffer = kzalloc(round16(tmp1.len) + round16(derived_key.len), gfp); | 
|  | if (!buffer) | 
|  | return -ENOMEM; | 
|  |  | 
|  | tmp1.data = buffer; | 
|  | derived_key.data = buffer + round16(tmp1.len); | 
|  |  | 
|  | ret = rfc3961_calc_H(krb5, octet_string, &tmp1, gfp); | 
|  | if (ret < 0) | 
|  | goto err; | 
|  |  | 
|  | tmp2.len = tmp1.len & ~(m - 1); | 
|  | tmp2.data = tmp1.data; | 
|  |  | 
|  | ret = rfc3961_calc_DK(krb5, protocol_key, &prfconstant, &derived_key, gfp); | 
|  | if (ret < 0) | 
|  | goto err; | 
|  |  | 
|  | ret = rfc3961_calc_E(krb5, &derived_key, &tmp2, result, gfp); | 
|  |  | 
|  | err: | 
|  | kfree_sensitive(buffer); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Derive the Ke and Ki keys and package them into a key parameter that can be | 
|  | * given to the setkey of a authenc AEAD crypto object. | 
|  | */ | 
|  | int authenc_derive_encrypt_keys(const struct krb5_enctype *krb5, | 
|  | const struct krb5_buffer *TK, | 
|  | unsigned int usage, | 
|  | struct krb5_buffer *setkey, | 
|  | gfp_t gfp) | 
|  | { | 
|  | struct crypto_authenc_key_param *param; | 
|  | struct krb5_buffer Ke, Ki; | 
|  | struct rtattr *rta; | 
|  | int ret; | 
|  |  | 
|  | Ke.len  = krb5->Ke_len; | 
|  | Ki.len  = krb5->Ki_len; | 
|  | setkey->len = RTA_LENGTH(sizeof(*param)) + Ke.len + Ki.len; | 
|  | setkey->data = kzalloc(setkey->len, GFP_KERNEL); | 
|  | if (!setkey->data) | 
|  | return -ENOMEM; | 
|  |  | 
|  | rta = setkey->data; | 
|  | rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM; | 
|  | rta->rta_len = RTA_LENGTH(sizeof(*param)); | 
|  | param = RTA_DATA(rta); | 
|  | param->enckeylen = htonl(Ke.len); | 
|  |  | 
|  | Ki.data = (void *)(param + 1); | 
|  | Ke.data = Ki.data + Ki.len; | 
|  |  | 
|  | ret = krb5_derive_Ke(krb5, TK, usage, &Ke, gfp); | 
|  | if (ret < 0) { | 
|  | pr_err("get_Ke failed %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  | ret = krb5_derive_Ki(krb5, TK, usage, &Ki, gfp); | 
|  | if (ret < 0) | 
|  | pr_err("get_Ki failed %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Package predefined Ke and Ki keys and into a key parameter that can be given | 
|  | * to the setkey of an authenc AEAD crypto object. | 
|  | */ | 
|  | int authenc_load_encrypt_keys(const struct krb5_enctype *krb5, | 
|  | const struct krb5_buffer *Ke, | 
|  | const struct krb5_buffer *Ki, | 
|  | struct krb5_buffer *setkey, | 
|  | gfp_t gfp) | 
|  | { | 
|  | struct crypto_authenc_key_param *param; | 
|  | struct rtattr *rta; | 
|  |  | 
|  | setkey->len = RTA_LENGTH(sizeof(*param)) + Ke->len + Ki->len; | 
|  | setkey->data = kzalloc(setkey->len, GFP_KERNEL); | 
|  | if (!setkey->data) | 
|  | return -ENOMEM; | 
|  |  | 
|  | rta = setkey->data; | 
|  | rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM; | 
|  | rta->rta_len = RTA_LENGTH(sizeof(*param)); | 
|  | param = RTA_DATA(rta); | 
|  | param->enckeylen = htonl(Ke->len); | 
|  | memcpy((void *)(param + 1), Ki->data, Ki->len); | 
|  | memcpy((void *)(param + 1) + Ki->len, Ke->data, Ke->len); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Derive the Kc key for checksum-only mode and package it into a key parameter | 
|  | * that can be given to the setkey of a hash crypto object. | 
|  | */ | 
|  | int rfc3961_derive_checksum_key(const struct krb5_enctype *krb5, | 
|  | const struct krb5_buffer *TK, | 
|  | unsigned int usage, | 
|  | struct krb5_buffer *setkey, | 
|  | gfp_t gfp) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | setkey->len = krb5->Kc_len; | 
|  | setkey->data = kzalloc(setkey->len, GFP_KERNEL); | 
|  | if (!setkey->data) | 
|  | return -ENOMEM; | 
|  |  | 
|  | ret = krb5_derive_Kc(krb5, TK, usage, setkey, gfp); | 
|  | if (ret < 0) | 
|  | pr_err("get_Kc failed %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Package a predefined Kc key for checksum-only mode into a key parameter that | 
|  | * can be given to the setkey of a hash crypto object. | 
|  | */ | 
|  | int rfc3961_load_checksum_key(const struct krb5_enctype *krb5, | 
|  | const struct krb5_buffer *Kc, | 
|  | struct krb5_buffer *setkey, | 
|  | gfp_t gfp) | 
|  | { | 
|  | setkey->len = krb5->Kc_len; | 
|  | setkey->data = kmemdup(Kc->data, Kc->len, GFP_KERNEL); | 
|  | if (!setkey->data) | 
|  | return -ENOMEM; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Apply encryption and checksumming functions to part of a scatterlist. | 
|  | */ | 
|  | ssize_t krb5_aead_encrypt(const struct krb5_enctype *krb5, | 
|  | struct crypto_aead *aead, | 
|  | struct scatterlist *sg, unsigned int nr_sg, size_t sg_len, | 
|  | size_t data_offset, size_t data_len, | 
|  | bool preconfounded) | 
|  | { | 
|  | struct aead_request *req; | 
|  | ssize_t ret, done; | 
|  | size_t bsize, base_len, secure_offset, secure_len, pad_len, cksum_offset; | 
|  | void *buffer; | 
|  | u8 *iv; | 
|  |  | 
|  | if (WARN_ON(data_offset != krb5->conf_len)) | 
|  | return -EINVAL; /* Data is in wrong place */ | 
|  |  | 
|  | secure_offset	= 0; | 
|  | base_len	= krb5->conf_len + data_len; | 
|  | pad_len		= 0; | 
|  | secure_len	= base_len + pad_len; | 
|  | cksum_offset	= secure_len; | 
|  | if (WARN_ON(cksum_offset + krb5->cksum_len > sg_len)) | 
|  | return -EFAULT; | 
|  |  | 
|  | bsize = krb5_aead_size(aead) + | 
|  | krb5_aead_ivsize(aead); | 
|  | buffer = kzalloc(bsize, GFP_NOFS); | 
|  | if (!buffer) | 
|  | return -ENOMEM; | 
|  |  | 
|  | /* Insert the confounder into the buffer */ | 
|  | ret = -EFAULT; | 
|  | if (!preconfounded) { | 
|  | get_random_bytes(buffer, krb5->conf_len); | 
|  | done = sg_pcopy_from_buffer(sg, nr_sg, buffer, krb5->conf_len, | 
|  | secure_offset); | 
|  | if (done != krb5->conf_len) | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | /* We may need to pad out to the crypto blocksize. */ | 
|  | if (pad_len) { | 
|  | done = sg_zero_buffer(sg, nr_sg, pad_len, data_offset + data_len); | 
|  | if (done != pad_len) | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | /* Hash and encrypt the message. */ | 
|  | req = buffer; | 
|  | iv = buffer + krb5_aead_size(aead); | 
|  |  | 
|  | aead_request_set_tfm(req, aead); | 
|  | aead_request_set_callback(req, 0, NULL, NULL); | 
|  | aead_request_set_crypt(req, sg, sg, secure_len, iv); | 
|  | ret = crypto_aead_encrypt(req); | 
|  | if (ret < 0) | 
|  | goto error; | 
|  |  | 
|  | ret = secure_len + krb5->cksum_len; | 
|  |  | 
|  | error: | 
|  | kfree_sensitive(buffer); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Apply decryption and checksumming functions to a message.  The offset and | 
|  | * length are updated to reflect the actual content of the encrypted region. | 
|  | */ | 
|  | int krb5_aead_decrypt(const struct krb5_enctype *krb5, | 
|  | struct crypto_aead *aead, | 
|  | struct scatterlist *sg, unsigned int nr_sg, | 
|  | size_t *_offset, size_t *_len) | 
|  | { | 
|  | struct aead_request *req; | 
|  | size_t bsize; | 
|  | void *buffer; | 
|  | int ret; | 
|  | u8 *iv; | 
|  |  | 
|  | if (WARN_ON(*_offset != 0)) | 
|  | return -EINVAL; /* Can't set offset on aead */ | 
|  |  | 
|  | if (*_len < krb5->conf_len + krb5->cksum_len) | 
|  | return -EPROTO; | 
|  |  | 
|  | bsize = krb5_aead_size(aead) + | 
|  | krb5_aead_ivsize(aead); | 
|  | buffer = kzalloc(bsize, GFP_NOFS); | 
|  | if (!buffer) | 
|  | return -ENOMEM; | 
|  |  | 
|  | /* Decrypt the message and verify its checksum. */ | 
|  | req = buffer; | 
|  | iv = buffer + krb5_aead_size(aead); | 
|  |  | 
|  | aead_request_set_tfm(req, aead); | 
|  | aead_request_set_callback(req, 0, NULL, NULL); | 
|  | aead_request_set_crypt(req, sg, sg, *_len, iv); | 
|  | ret = crypto_aead_decrypt(req); | 
|  | if (ret < 0) | 
|  | goto error; | 
|  |  | 
|  | /* Adjust the boundaries of the data. */ | 
|  | *_offset += krb5->conf_len; | 
|  | *_len -= krb5->conf_len + krb5->cksum_len; | 
|  | ret = 0; | 
|  |  | 
|  | error: | 
|  | kfree_sensitive(buffer); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Generate a checksum over some metadata and part of an skbuff and insert the | 
|  | * MIC into the skbuff immediately prior to the data. | 
|  | */ | 
|  | ssize_t rfc3961_get_mic(const struct krb5_enctype *krb5, | 
|  | struct crypto_shash *shash, | 
|  | const struct krb5_buffer *metadata, | 
|  | struct scatterlist *sg, unsigned int nr_sg, size_t sg_len, | 
|  | size_t data_offset, size_t data_len) | 
|  | { | 
|  | struct shash_desc *desc; | 
|  | ssize_t ret, done; | 
|  | size_t bsize; | 
|  | void *buffer, *digest; | 
|  |  | 
|  | if (WARN_ON(data_offset != krb5->cksum_len)) | 
|  | return -EMSGSIZE; | 
|  |  | 
|  | bsize = krb5_shash_size(shash) + | 
|  | krb5_digest_size(shash); | 
|  | buffer = kzalloc(bsize, GFP_NOFS); | 
|  | if (!buffer) | 
|  | return -ENOMEM; | 
|  |  | 
|  | /* Calculate the MIC with key Kc and store it into the skb */ | 
|  | desc = buffer; | 
|  | desc->tfm = shash; | 
|  | ret = crypto_shash_init(desc); | 
|  | if (ret < 0) | 
|  | goto error; | 
|  |  | 
|  | if (metadata) { | 
|  | ret = crypto_shash_update(desc, metadata->data, metadata->len); | 
|  | if (ret < 0) | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | ret = crypto_shash_update_sg(desc, sg, data_offset, data_len); | 
|  | if (ret < 0) | 
|  | goto error; | 
|  |  | 
|  | digest = buffer + krb5_shash_size(shash); | 
|  | ret = crypto_shash_final(desc, digest); | 
|  | if (ret < 0) | 
|  | goto error; | 
|  |  | 
|  | ret = -EFAULT; | 
|  | done = sg_pcopy_from_buffer(sg, nr_sg, digest, krb5->cksum_len, | 
|  | data_offset - krb5->cksum_len); | 
|  | if (done != krb5->cksum_len) | 
|  | goto error; | 
|  |  | 
|  | ret = krb5->cksum_len + data_len; | 
|  |  | 
|  | error: | 
|  | kfree_sensitive(buffer); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Check the MIC on a region of an skbuff.  The offset and length are updated | 
|  | * to reflect the actual content of the secure region. | 
|  | */ | 
|  | int rfc3961_verify_mic(const struct krb5_enctype *krb5, | 
|  | struct crypto_shash *shash, | 
|  | const struct krb5_buffer *metadata, | 
|  | struct scatterlist *sg, unsigned int nr_sg, | 
|  | size_t *_offset, size_t *_len) | 
|  | { | 
|  | struct shash_desc *desc; | 
|  | ssize_t done; | 
|  | size_t bsize, data_offset, data_len, offset = *_offset, len = *_len; | 
|  | void *buffer = NULL; | 
|  | int ret; | 
|  | u8 *cksum, *cksum2; | 
|  |  | 
|  | if (len < krb5->cksum_len) | 
|  | return -EPROTO; | 
|  | data_offset = offset + krb5->cksum_len; | 
|  | data_len = len - krb5->cksum_len; | 
|  |  | 
|  | bsize = krb5_shash_size(shash) + | 
|  | krb5_digest_size(shash) * 2; | 
|  | buffer = kzalloc(bsize, GFP_NOFS); | 
|  | if (!buffer) | 
|  | return -ENOMEM; | 
|  |  | 
|  | cksum = buffer + | 
|  | krb5_shash_size(shash); | 
|  | cksum2 = buffer + | 
|  | krb5_shash_size(shash) + | 
|  | krb5_digest_size(shash); | 
|  |  | 
|  | /* Calculate the MIC */ | 
|  | desc = buffer; | 
|  | desc->tfm = shash; | 
|  | ret = crypto_shash_init(desc); | 
|  | if (ret < 0) | 
|  | goto error; | 
|  |  | 
|  | if (metadata) { | 
|  | ret = crypto_shash_update(desc, metadata->data, metadata->len); | 
|  | if (ret < 0) | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | crypto_shash_update_sg(desc, sg, data_offset, data_len); | 
|  | crypto_shash_final(desc, cksum); | 
|  |  | 
|  | ret = -EFAULT; | 
|  | done = sg_pcopy_to_buffer(sg, nr_sg, cksum2, krb5->cksum_len, offset); | 
|  | if (done != krb5->cksum_len) | 
|  | goto error; | 
|  |  | 
|  | if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) { | 
|  | ret = -EBADMSG; | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | *_offset += krb5->cksum_len; | 
|  | *_len -= krb5->cksum_len; | 
|  | ret = 0; | 
|  |  | 
|  | error: | 
|  | kfree_sensitive(buffer); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | const struct krb5_crypto_profile rfc3961_simplified_profile = { | 
|  | .calc_PRF		= rfc3961_calc_PRF, | 
|  | .calc_Kc		= rfc3961_calc_DK, | 
|  | .calc_Ke		= rfc3961_calc_DK, | 
|  | .calc_Ki		= rfc3961_calc_DK, | 
|  | .derive_encrypt_keys	= authenc_derive_encrypt_keys, | 
|  | .load_encrypt_keys	= authenc_load_encrypt_keys, | 
|  | .derive_checksum_key	= rfc3961_derive_checksum_key, | 
|  | .load_checksum_key	= rfc3961_load_checksum_key, | 
|  | .encrypt		= krb5_aead_encrypt, | 
|  | .decrypt		= krb5_aead_decrypt, | 
|  | .get_mic		= rfc3961_get_mic, | 
|  | .verify_mic		= rfc3961_verify_mic, | 
|  | }; |