| 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | /* Kerberos key derivation. |
| 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/export.h> |
| 11 | #include <linux/slab.h> |
| 12 | #include <crypto/skcipher.h> |
| 13 | #include <crypto/hash.h> |
| 14 | #include "internal.h" |
| 15 | |
| 16 | /** |
| 17 | * crypto_krb5_calc_PRFplus - Calculate PRF+ [RFC4402] |
| 18 | * @krb5: The encryption type to use |
| 19 | * @K: The protocol key for the pseudo-random function |
| 20 | * @L: The length of the output |
| 21 | * @S: The input octet string |
| 22 | * @result: Result buffer, sized to krb5->prf_len |
| 23 | * @gfp: Allocation restrictions |
| 24 | * |
| 25 | * Calculate the kerberos pseudo-random function, PRF+() by the following |
| 26 | * method: |
| 27 | * |
| 28 | * PRF+(K, L, S) = truncate(L, T1 || T2 || .. || Tn) |
| 29 | * Tn = PRF(K, n || S) |
| 30 | * [rfc4402 sec 2] |
| 31 | */ |
| 32 | int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5, |
| 33 | const struct krb5_buffer *K, |
| 34 | unsigned int L, |
| 35 | const struct krb5_buffer *S, |
| 36 | struct krb5_buffer *result, |
| 37 | gfp_t gfp) |
| 38 | { |
| 39 | struct krb5_buffer T_series, Tn, n_S; |
| 40 | void *buffer; |
| 41 | int ret, n = 1; |
| 42 | |
| 43 | Tn.len = krb5->prf_len; |
| 44 | T_series.len = 0; |
| 45 | n_S.len = 4 + S->len; |
| 46 | |
| 47 | buffer = kzalloc(round16(L + Tn.len) + round16(n_S.len), gfp); |
| 48 | if (!buffer) |
| 49 | return -ENOMEM; |
| 50 | |
| 51 | T_series.data = buffer; |
| 52 | n_S.data = buffer + round16(L + Tn.len); |
| 53 | memcpy(n_S.data + 4, S->data, S->len); |
| 54 | |
| 55 | while (T_series.len < L) { |
| 56 | *(__be32 *)(n_S.data) = htonl(n); |
| 57 | Tn.data = T_series.data + Tn.len * (n - 1); |
| 58 | ret = krb5->profile->calc_PRF(krb5, K, &n_S, &Tn, gfp); |
| 59 | if (ret < 0) |
| 60 | goto err; |
| 61 | T_series.len += Tn.len; |
| 62 | n++; |
| 63 | } |
| 64 | |
| 65 | /* Truncate to L */ |
| 66 | memcpy(result->data, T_series.data, L); |
| 67 | ret = 0; |
| 68 | |
| 69 | err: |
| 70 | kfree_sensitive(objp: buffer); |
| 71 | return ret; |
| 72 | } |
| 73 | EXPORT_SYMBOL(crypto_krb5_calc_PRFplus); |
| 74 | |
| 75 | /** |
| 76 | * krb5_derive_Kc - Derive key Kc and install into a hash |
| 77 | * @krb5: The encryption type to use |
| 78 | * @TK: The base key |
| 79 | * @usage: The key usage number |
| 80 | * @key: Prepped buffer to store the key into |
| 81 | * @gfp: Allocation restrictions |
| 82 | * |
| 83 | * Derive the Kerberos Kc checksumming key. The key is stored into the |
| 84 | * prepared buffer. |
| 85 | */ |
| 86 | int krb5_derive_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *TK, |
| 87 | u32 usage, struct krb5_buffer *key, gfp_t gfp) |
| 88 | { |
| 89 | u8 buf[5] __aligned(CRYPTO_MINALIGN); |
| 90 | struct krb5_buffer usage_constant = { .len = 5, .data = buf }; |
| 91 | |
| 92 | *(__be32 *)buf = cpu_to_be32(usage); |
| 93 | buf[4] = KEY_USAGE_SEED_CHECKSUM; |
| 94 | |
| 95 | key->len = krb5->Kc_len; |
| 96 | return krb5->profile->calc_Kc(krb5, TK, &usage_constant, key, gfp); |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * krb5_derive_Ke - Derive key Ke and install into an skcipher |
| 101 | * @krb5: The encryption type to use |
| 102 | * @TK: The base key |
| 103 | * @usage: The key usage number |
| 104 | * @key: Prepped buffer to store the key into |
| 105 | * @gfp: Allocation restrictions |
| 106 | * |
| 107 | * Derive the Kerberos Ke encryption key. The key is stored into the prepared |
| 108 | * buffer. |
| 109 | */ |
| 110 | int krb5_derive_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *TK, |
| 111 | u32 usage, struct krb5_buffer *key, gfp_t gfp) |
| 112 | { |
| 113 | u8 buf[5] __aligned(CRYPTO_MINALIGN); |
| 114 | struct krb5_buffer usage_constant = { .len = 5, .data = buf }; |
| 115 | |
| 116 | *(__be32 *)buf = cpu_to_be32(usage); |
| 117 | buf[4] = KEY_USAGE_SEED_ENCRYPTION; |
| 118 | |
| 119 | key->len = krb5->Ke_len; |
| 120 | return krb5->profile->calc_Ke(krb5, TK, &usage_constant, key, gfp); |
| 121 | } |
| 122 | |
| 123 | /** |
| 124 | * krb5_derive_Ki - Derive key Ki and install into a hash |
| 125 | * @krb5: The encryption type to use |
| 126 | * @TK: The base key |
| 127 | * @usage: The key usage number |
| 128 | * @key: Prepped buffer to store the key into |
| 129 | * @gfp: Allocation restrictions |
| 130 | * |
| 131 | * Derive the Kerberos Ki integrity checksum key. The key is stored into the |
| 132 | * prepared buffer. |
| 133 | */ |
| 134 | int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK, |
| 135 | u32 usage, struct krb5_buffer *key, gfp_t gfp) |
| 136 | { |
| 137 | u8 buf[5] __aligned(CRYPTO_MINALIGN); |
| 138 | struct krb5_buffer usage_constant = { .len = 5, .data = buf }; |
| 139 | |
| 140 | *(__be32 *)buf = cpu_to_be32(usage); |
| 141 | buf[4] = KEY_USAGE_SEED_INTEGRITY; |
| 142 | |
| 143 | key->len = krb5->Ki_len; |
| 144 | return krb5->profile->calc_Ki(krb5, TK, &usage_constant, key, gfp); |
| 145 | } |
| 146 | |