| // 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, |
| }; |