--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* rfc6803 Camellia Encryption for Kerberos 5
+ *
+ * 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/slab.h>
+#include "internal.h"
+
+/*
+ * Calculate the key derivation function KDF-FEEDBACK_CMAC(key, constant)
+ *
+ *     n = ceiling(k / 128)
+ *     K(0) = zeros
+ *     K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k)
+ *     DR(key, constant) = k-truncate(K(1) | K(2) | ... | K(n))
+ *     KDF-FEEDBACK-CMAC(key, constant) = random-to-key(DR(key, constant))
+ *
+ *     [rfc6803 sec 3]
+ */
+static int rfc6803_calc_KDF_FEEDBACK_CMAC(const struct krb5_enctype *krb5,
+                                         const struct krb5_buffer *key,
+                                         const struct krb5_buffer *constant,
+                                         struct krb5_buffer *result,
+                                         gfp_t gfp)
+{
+       struct crypto_shash *shash;
+       struct krb5_buffer K, data;
+       struct shash_desc *desc;
+       __be32 tmp;
+       size_t bsize, offset, seg;
+       void *buffer;
+       u32 i = 0, k = result->len * 8;
+       u8 *p;
+       int ret = -ENOMEM;
+
+       shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+       if (IS_ERR(shash))
+               return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+       ret = crypto_shash_setkey(shash, key->data, key->len);
+       if (ret < 0)
+               goto error_shash;
+
+       ret = -ENOMEM;
+       K.len = crypto_shash_digestsize(shash);
+       data.len = K.len + 4 + constant->len + 1 + 4;
+       bsize = krb5_shash_size(shash) +
+               krb5_digest_size(shash) +
+               crypto_roundup(K.len) +
+               crypto_roundup(data.len);
+       buffer = kzalloc(bsize, GFP_NOFS);
+       if (!buffer)
+               goto error_shash;
+
+       desc = buffer;
+       desc->tfm = shash;
+
+       K.data = buffer +
+               krb5_shash_size(shash) +
+               krb5_digest_size(shash);
+       data.data = buffer +
+               krb5_shash_size(shash) +
+               krb5_digest_size(shash) +
+               crypto_roundup(K.len);
+
+       p = data.data + K.len + 4;
+       memcpy(p, constant->data, constant->len);
+       p += constant->len;
+       *p++ = 0x00;
+       tmp = htonl(k);
+       memcpy(p, &tmp, 4);
+       p += 4;
+
+       ret = -EINVAL;
+       if (WARN_ON(p - (u8 *)data.data != data.len))
+               goto error;
+
+       offset = 0;
+       do {
+               i++;
+               p = data.data;
+               memcpy(p, K.data, K.len);
+               p += K.len;
+               *(__be32 *)p = htonl(i);
+
+               ret = crypto_shash_init(desc);
+               if (ret < 0)
+                       goto error;
+               ret = crypto_shash_finup(desc, data.data, data.len, K.data);
+               if (ret < 0)
+                       goto error;
+
+               seg = min_t(size_t, result->len - offset, K.len);
+               memcpy(result->data + offset, K.data, seg);
+               offset += seg;
+       } while (offset < result->len);
+
+error:
+       kfree_sensitive(buffer);
+error_shash:
+       crypto_free_shash(shash);
+       return ret;
+}
+
+/*
+ * Calculate the pseudo-random function, PRF().
+ *
+ *     Kp = KDF-FEEDBACK-CMAC(protocol-key, "prf")
+ *     PRF = CMAC(Kp, octet-string)
+ *      [rfc6803 sec 6]
+ */
+static int rfc6803_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 crypto_shash *shash;
+       struct krb5_buffer Kp;
+       struct shash_desc *desc;
+       size_t bsize;
+       void *buffer;
+       int ret;
+
+       Kp.len = krb5->prf_len;
+
+       shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+       if (IS_ERR(shash))
+               return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+
+       ret = -EINVAL;
+       if (result->len != crypto_shash_digestsize(shash))
+               goto out_shash;
+
+       ret = -ENOMEM;
+       bsize = krb5_shash_size(shash) +
+               krb5_digest_size(shash) +
+               crypto_roundup(Kp.len);
+       buffer = kzalloc(bsize, GFP_NOFS);
+       if (!buffer)
+               goto out_shash;
+
+       Kp.data = buffer +
+               krb5_shash_size(shash) +
+               krb5_digest_size(shash);
+
+       ret = rfc6803_calc_KDF_FEEDBACK_CMAC(krb5, protocol_key, &prfconstant,
+                                            &Kp, gfp);
+       if (ret < 0)
+               goto out;
+
+       ret = crypto_shash_setkey(shash, Kp.data, Kp.len);
+       if (ret < 0)
+               goto out;
+
+       desc = buffer;
+       desc->tfm = shash;
+       ret = crypto_shash_init(desc);
+       if (ret < 0)
+               goto out;
+
+       ret = crypto_shash_finup(desc, octet_string->data, octet_string->len, result->data);
+       if (ret < 0)
+               goto out;
+
+out:
+       kfree_sensitive(buffer);
+out_shash:
+       crypto_free_shash(shash);
+       return ret;
+}
+
+
+static const struct krb5_crypto_profile rfc6803_crypto_profile = {
+       .calc_PRF               = rfc6803_calc_PRF,
+       .calc_Kc                = rfc6803_calc_KDF_FEEDBACK_CMAC,
+       .calc_Ke                = rfc6803_calc_KDF_FEEDBACK_CMAC,
+       .calc_Ki                = rfc6803_calc_KDF_FEEDBACK_CMAC,
+       .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,
+};
+
+const struct krb5_enctype krb5_camellia128_cts_cmac = {
+       .etype          = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+       .ctype          = KRB5_CKSUMTYPE_CMAC_CAMELLIA128,
+       .name           = "camellia128-cts-cmac",
+       .encrypt_name   = "krb5enc(cmac(camellia),cts(cbc(camellia)))",
+       .cksum_name     = "cmac(camellia)",
+       .hash_name      = NULL,
+       .derivation_enc = "cts(cbc(camellia))",
+       .key_bytes      = 16,
+       .key_len        = 16,
+       .Kc_len         = 16,
+       .Ke_len         = 16,
+       .Ki_len         = 16,
+       .block_len      = 16,
+       .conf_len       = 16,
+       .cksum_len      = 16,
+       .hash_len       = 16,
+       .prf_len        = 16,
+       .keyed_cksum    = true,
+       .random_to_key  = NULL, /* Identity */
+       .profile        = &rfc6803_crypto_profile,
+};
+
+const struct krb5_enctype krb5_camellia256_cts_cmac = {
+       .etype          = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+       .ctype          = KRB5_CKSUMTYPE_CMAC_CAMELLIA256,
+       .name           = "camellia256-cts-cmac",
+       .encrypt_name   = "krb5enc(cmac(camellia),cts(cbc(camellia)))",
+       .cksum_name     = "cmac(camellia)",
+       .hash_name      = NULL,
+       .derivation_enc = "cts(cbc(camellia))",
+       .key_bytes      = 32,
+       .key_len        = 32,
+       .Kc_len         = 32,
+       .Ke_len         = 32,
+       .Ki_len         = 32,
+       .block_len      = 16,
+       .conf_len       = 16,
+       .cksum_len      = 16,
+       .hash_len       = 16,
+       .prf_len        = 16,
+       .keyed_cksum    = true,
+       .random_to_key  = NULL, /* Identity */
+       .profile        = &rfc6803_crypto_profile,
+};