1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* rfc6803 Camellia Encryption for Kerberos 5 3 * 4 * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. 5 * Written by David Howells (dhowells@redhat.com) 6 */ 7 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10 #include <linux/slab.h> 11 #include "internal.h" 12 13 /* 14 * Calculate the key derivation function KDF-FEEDBACK_CMAC(key, constant) 15 * 16 * n = ceiling(k / 128) 17 * K(0) = zeros 18 * K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k) 19 * DR(key, constant) = k-truncate(K(1) | K(2) | ... | K(n)) 20 * KDF-FEEDBACK-CMAC(key, constant) = random-to-key(DR(key, constant)) 21 * 22 * [rfc6803 sec 3] 23 */ 24 static int rfc6803_calc_KDF_FEEDBACK_CMAC(const struct krb5_enctype *krb5, 25 const struct krb5_buffer *key, 26 const struct krb5_buffer *constant, 27 struct krb5_buffer *result, 28 gfp_t gfp) 29 { 30 struct crypto_shash *shash; 31 struct krb5_buffer K, data; 32 struct shash_desc *desc; 33 __be32 tmp; 34 size_t bsize, offset, seg; 35 void *buffer; 36 u32 i = 0, k = result->len * 8; 37 u8 *p; 38 int ret = -ENOMEM; 39 40 shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); 41 if (IS_ERR(shash)) 42 return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); 43 ret = crypto_shash_setkey(shash, key->data, key->len); 44 if (ret < 0) 45 goto error_shash; 46 47 ret = -ENOMEM; 48 K.len = crypto_shash_digestsize(shash); 49 data.len = K.len + 4 + constant->len + 1 + 4; 50 bsize = krb5_shash_size(shash) + 51 krb5_digest_size(shash) + 52 crypto_roundup(K.len) + 53 crypto_roundup(data.len); 54 buffer = kzalloc(bsize, GFP_NOFS); 55 if (!buffer) 56 goto error_shash; 57 58 desc = buffer; 59 desc->tfm = shash; 60 61 K.data = buffer + 62 krb5_shash_size(shash) + 63 krb5_digest_size(shash); 64 data.data = buffer + 65 krb5_shash_size(shash) + 66 krb5_digest_size(shash) + 67 crypto_roundup(K.len); 68 69 p = data.data + K.len + 4; 70 memcpy(p, constant->data, constant->len); 71 p += constant->len; 72 *p++ = 0x00; 73 tmp = htonl(k); 74 memcpy(p, &tmp, 4); 75 p += 4; 76 77 ret = -EINVAL; 78 if (WARN_ON(p - (u8 *)data.data != data.len)) 79 goto error; 80 81 offset = 0; 82 do { 83 i++; 84 p = data.data; 85 memcpy(p, K.data, K.len); 86 p += K.len; 87 *(__be32 *)p = htonl(i); 88 89 ret = crypto_shash_init(desc); 90 if (ret < 0) 91 goto error; 92 ret = crypto_shash_finup(desc, data.data, data.len, K.data); 93 if (ret < 0) 94 goto error; 95 96 seg = min_t(size_t, result->len - offset, K.len); 97 memcpy(result->data + offset, K.data, seg); 98 offset += seg; 99 } while (offset < result->len); 100 101 error: 102 kfree_sensitive(buffer); 103 error_shash: 104 crypto_free_shash(shash); 105 return ret; 106 } 107 108 /* 109 * Calculate the pseudo-random function, PRF(). 110 * 111 * Kp = KDF-FEEDBACK-CMAC(protocol-key, "prf") 112 * PRF = CMAC(Kp, octet-string) 113 * [rfc6803 sec 6] 114 */ 115 static int rfc6803_calc_PRF(const struct krb5_enctype *krb5, 116 const struct krb5_buffer *protocol_key, 117 const struct krb5_buffer *octet_string, 118 struct krb5_buffer *result, 119 gfp_t gfp) 120 { 121 static const struct krb5_buffer prfconstant = { 3, "prf" }; 122 struct crypto_shash *shash; 123 struct krb5_buffer Kp; 124 struct shash_desc *desc; 125 size_t bsize; 126 void *buffer; 127 int ret; 128 129 Kp.len = krb5->prf_len; 130 131 shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); 132 if (IS_ERR(shash)) 133 return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); 134 135 ret = -EINVAL; 136 if (result->len != crypto_shash_digestsize(shash)) 137 goto out_shash; 138 139 ret = -ENOMEM; 140 bsize = krb5_shash_size(shash) + 141 krb5_digest_size(shash) + 142 crypto_roundup(Kp.len); 143 buffer = kzalloc(bsize, GFP_NOFS); 144 if (!buffer) 145 goto out_shash; 146 147 Kp.data = buffer + 148 krb5_shash_size(shash) + 149 krb5_digest_size(shash); 150 151 ret = rfc6803_calc_KDF_FEEDBACK_CMAC(krb5, protocol_key, &prfconstant, 152 &Kp, gfp); 153 if (ret < 0) 154 goto out; 155 156 ret = crypto_shash_setkey(shash, Kp.data, Kp.len); 157 if (ret < 0) 158 goto out; 159 160 desc = buffer; 161 desc->tfm = shash; 162 ret = crypto_shash_init(desc); 163 if (ret < 0) 164 goto out; 165 166 ret = crypto_shash_finup(desc, octet_string->data, octet_string->len, result->data); 167 if (ret < 0) 168 goto out; 169 170 out: 171 kfree_sensitive(buffer); 172 out_shash: 173 crypto_free_shash(shash); 174 return ret; 175 } 176 177 178 static const struct krb5_crypto_profile rfc6803_crypto_profile = { 179 .calc_PRF = rfc6803_calc_PRF, 180 .calc_Kc = rfc6803_calc_KDF_FEEDBACK_CMAC, 181 .calc_Ke = rfc6803_calc_KDF_FEEDBACK_CMAC, 182 .calc_Ki = rfc6803_calc_KDF_FEEDBACK_CMAC, 183 .derive_encrypt_keys = authenc_derive_encrypt_keys, 184 .load_encrypt_keys = authenc_load_encrypt_keys, 185 .derive_checksum_key = rfc3961_derive_checksum_key, 186 .load_checksum_key = rfc3961_load_checksum_key, 187 .encrypt = krb5_aead_encrypt, 188 .decrypt = krb5_aead_decrypt, 189 .get_mic = rfc3961_get_mic, 190 .verify_mic = rfc3961_verify_mic, 191 }; 192 193 const struct krb5_enctype krb5_camellia128_cts_cmac = { 194 .etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC, 195 .ctype = KRB5_CKSUMTYPE_CMAC_CAMELLIA128, 196 .name = "camellia128-cts-cmac", 197 .encrypt_name = "krb5enc(cmac(camellia),cts(cbc(camellia)))", 198 .cksum_name = "cmac(camellia)", 199 .hash_name = NULL, 200 .derivation_enc = "cts(cbc(camellia))", 201 .key_bytes = 16, 202 .key_len = 16, 203 .Kc_len = 16, 204 .Ke_len = 16, 205 .Ki_len = 16, 206 .block_len = 16, 207 .conf_len = 16, 208 .cksum_len = 16, 209 .hash_len = 16, 210 .prf_len = 16, 211 .keyed_cksum = true, 212 .random_to_key = NULL, /* Identity */ 213 .profile = &rfc6803_crypto_profile, 214 }; 215 216 const struct krb5_enctype krb5_camellia256_cts_cmac = { 217 .etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC, 218 .ctype = KRB5_CKSUMTYPE_CMAC_CAMELLIA256, 219 .name = "camellia256-cts-cmac", 220 .encrypt_name = "krb5enc(cmac(camellia),cts(cbc(camellia)))", 221 .cksum_name = "cmac(camellia)", 222 .hash_name = NULL, 223 .derivation_enc = "cts(cbc(camellia))", 224 .key_bytes = 32, 225 .key_len = 32, 226 .Kc_len = 32, 227 .Ke_len = 32, 228 .Ki_len = 32, 229 .block_len = 16, 230 .conf_len = 16, 231 .cksum_len = 16, 232 .hash_len = 16, 233 .prf_len = 16, 234 .keyed_cksum = true, 235 .random_to_key = NULL, /* Identity */ 236 .profile = &rfc6803_crypto_profile, 237 }; 238