1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* RxGK transport 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/key-type.h>
11 #include <linux/slab.h>
12 #include <keys/rxrpc-type.h>
13 #include "ar-internal.h"
14 #include "rxgk_common.h"
15
16 #define round16(x) (((x) + 15) & ~15)
17
18 /*
19 * Constants used to derive the keys and hmacs actually used for doing stuff.
20 */
21 #define RXGK_CLIENT_ENC_PACKET 1026U // 0x402
22 #define RXGK_CLIENT_MIC_PACKET 1027U // 0x403
23 #define RXGK_SERVER_ENC_PACKET 1028U // 0x404
24 #define RXGK_SERVER_MIC_PACKET 1029U // 0x405
25 #define RXGK_CLIENT_ENC_RESPONSE 1030U // 0x406
26 #define RXGK_SERVER_ENC_TOKEN 1036U // 0x40c
27
rxgk_free(struct rxgk_context * gk)28 static void rxgk_free(struct rxgk_context *gk)
29 {
30 if (gk->tx_Kc)
31 crypto_free_shash(gk->tx_Kc);
32 if (gk->rx_Kc)
33 crypto_free_shash(gk->rx_Kc);
34 if (gk->tx_enc)
35 crypto_free_aead(gk->tx_enc);
36 if (gk->rx_enc)
37 crypto_free_aead(gk->rx_enc);
38 if (gk->resp_enc)
39 crypto_free_aead(gk->resp_enc);
40 kfree(gk);
41 }
42
rxgk_put(struct rxgk_context * gk)43 void rxgk_put(struct rxgk_context *gk)
44 {
45 if (gk && refcount_dec_and_test(&gk->usage))
46 rxgk_free(gk);
47 }
48
49 /*
50 * Transport key derivation function.
51 *
52 * TK = random-to-key(PRF+(K0, L,
53 * epoch || cid || start_time || key_number))
54 * [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-11 sec 8.3]
55 */
rxgk_derive_transport_key(struct rxrpc_connection * conn,struct rxgk_context * gk,const struct rxgk_key * rxgk,struct krb5_buffer * TK,gfp_t gfp)56 static int rxgk_derive_transport_key(struct rxrpc_connection *conn,
57 struct rxgk_context *gk,
58 const struct rxgk_key *rxgk,
59 struct krb5_buffer *TK,
60 gfp_t gfp)
61 {
62 const struct krb5_enctype *krb5 = gk->krb5;
63 struct krb5_buffer conn_info;
64 unsigned int L = krb5->key_bytes;
65 __be32 *info;
66 u8 *buffer;
67 int ret;
68
69 _enter("");
70
71 conn_info.len = sizeof(__be32) * 5;
72
73 buffer = kzalloc(round16(conn_info.len), gfp);
74 if (!buffer)
75 return -ENOMEM;
76
77 conn_info.data = buffer;
78
79 info = (__be32 *)conn_info.data;
80 info[0] = htonl(conn->proto.epoch);
81 info[1] = htonl(conn->proto.cid);
82 info[2] = htonl(conn->rxgk.start_time >> 32);
83 info[3] = htonl(conn->rxgk.start_time >> 0);
84 info[4] = htonl(gk->key_number);
85
86 ret = crypto_krb5_calc_PRFplus(krb5, &rxgk->key, L, &conn_info, TK, gfp);
87 kfree_sensitive(buffer);
88 _leave(" = %d", ret);
89 return ret;
90 }
91
92 /*
93 * Set up the ciphers for the usage keys.
94 */
rxgk_set_up_ciphers(struct rxrpc_connection * conn,struct rxgk_context * gk,const struct rxgk_key * rxgk,gfp_t gfp)95 static int rxgk_set_up_ciphers(struct rxrpc_connection *conn,
96 struct rxgk_context *gk,
97 const struct rxgk_key *rxgk,
98 gfp_t gfp)
99 {
100 const struct krb5_enctype *krb5 = gk->krb5;
101 struct crypto_shash *shash;
102 struct crypto_aead *aead;
103 struct krb5_buffer TK;
104 bool service = rxrpc_conn_is_service(conn);
105 int ret;
106 u8 *buffer;
107
108 buffer = kzalloc(krb5->key_bytes, gfp);
109 if (!buffer)
110 return -ENOMEM;
111
112 TK.len = krb5->key_bytes;
113 TK.data = buffer;
114
115 ret = rxgk_derive_transport_key(conn, gk, rxgk, &TK, gfp);
116 if (ret < 0)
117 goto out;
118
119 aead = crypto_krb5_prepare_encryption(krb5, &TK, RXGK_CLIENT_ENC_RESPONSE, gfp);
120 if (IS_ERR(aead))
121 goto aead_error;
122 gk->resp_enc = aead;
123
124 if (crypto_aead_blocksize(gk->resp_enc) != krb5->block_len ||
125 crypto_aead_authsize(gk->resp_enc) != krb5->cksum_len) {
126 pr_notice("algo inconsistent with krb5 table %u!=%u or %u!=%u\n",
127 crypto_aead_blocksize(gk->resp_enc), krb5->block_len,
128 crypto_aead_authsize(gk->resp_enc), krb5->cksum_len);
129 ret = -EINVAL;
130 goto out;
131 }
132
133 if (service) {
134 switch (conn->security_level) {
135 case RXRPC_SECURITY_AUTH:
136 shash = crypto_krb5_prepare_checksum(
137 krb5, &TK, RXGK_SERVER_MIC_PACKET, gfp);
138 if (IS_ERR(shash))
139 goto hash_error;
140 gk->tx_Kc = shash;
141 shash = crypto_krb5_prepare_checksum(
142 krb5, &TK, RXGK_CLIENT_MIC_PACKET, gfp);
143 if (IS_ERR(shash))
144 goto hash_error;
145 gk->rx_Kc = shash;
146 break;
147 case RXRPC_SECURITY_ENCRYPT:
148 aead = crypto_krb5_prepare_encryption(
149 krb5, &TK, RXGK_SERVER_ENC_PACKET, gfp);
150 if (IS_ERR(aead))
151 goto aead_error;
152 gk->tx_enc = aead;
153 aead = crypto_krb5_prepare_encryption(
154 krb5, &TK, RXGK_CLIENT_ENC_PACKET, gfp);
155 if (IS_ERR(aead))
156 goto aead_error;
157 gk->rx_enc = aead;
158 break;
159 }
160 } else {
161 switch (conn->security_level) {
162 case RXRPC_SECURITY_AUTH:
163 shash = crypto_krb5_prepare_checksum(
164 krb5, &TK, RXGK_CLIENT_MIC_PACKET, gfp);
165 if (IS_ERR(shash))
166 goto hash_error;
167 gk->tx_Kc = shash;
168 shash = crypto_krb5_prepare_checksum(
169 krb5, &TK, RXGK_SERVER_MIC_PACKET, gfp);
170 if (IS_ERR(shash))
171 goto hash_error;
172 gk->rx_Kc = shash;
173 break;
174 case RXRPC_SECURITY_ENCRYPT:
175 aead = crypto_krb5_prepare_encryption(
176 krb5, &TK, RXGK_CLIENT_ENC_PACKET, gfp);
177 if (IS_ERR(aead))
178 goto aead_error;
179 gk->tx_enc = aead;
180 aead = crypto_krb5_prepare_encryption(
181 krb5, &TK, RXGK_SERVER_ENC_PACKET, gfp);
182 if (IS_ERR(aead))
183 goto aead_error;
184 gk->rx_enc = aead;
185 break;
186 }
187 }
188
189 ret = 0;
190 out:
191 kfree_sensitive(buffer);
192 return ret;
193 aead_error:
194 ret = PTR_ERR(aead);
195 goto out;
196 hash_error:
197 ret = PTR_ERR(shash);
198 goto out;
199 }
200
201 /*
202 * Derive a transport key for a connection and then derive a bunch of usage
203 * keys from it and set up ciphers using them.
204 */
rxgk_generate_transport_key(struct rxrpc_connection * conn,const struct rxgk_key * key,unsigned int key_number,gfp_t gfp)205 struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *conn,
206 const struct rxgk_key *key,
207 unsigned int key_number,
208 gfp_t gfp)
209 {
210 struct rxgk_context *gk;
211 unsigned long lifetime;
212 int ret = -ENOPKG;
213
214 _enter("");
215
216 gk = kzalloc(sizeof(*gk), GFP_KERNEL);
217 if (!gk)
218 return ERR_PTR(-ENOMEM);
219 refcount_set(&gk->usage, 1);
220 gk->key = key;
221 gk->key_number = key_number;
222
223 gk->krb5 = crypto_krb5_find_enctype(key->enctype);
224 if (!gk->krb5)
225 goto err_tk;
226
227 ret = rxgk_set_up_ciphers(conn, gk, key, gfp);
228 if (ret)
229 goto err_tk;
230
231 /* Set the remaining number of bytes encrypted with this key that may
232 * be transmitted before rekeying. Note that the spec has been
233 * interpreted differently on this point...
234 */
235 switch (key->bytelife) {
236 case 0:
237 case 63:
238 gk->bytes_remaining = LLONG_MAX;
239 break;
240 case 1 ... 62:
241 gk->bytes_remaining = 1LL << key->bytelife;
242 break;
243 default:
244 gk->bytes_remaining = key->bytelife;
245 break;
246 }
247
248 /* Set the time after which rekeying must occur */
249 if (key->lifetime) {
250 lifetime = min_t(u64, key->lifetime, INT_MAX / HZ);
251 lifetime *= HZ;
252 } else {
253 lifetime = MAX_JIFFY_OFFSET;
254 }
255 gk->expiry = jiffies + lifetime;
256 return gk;
257
258 err_tk:
259 rxgk_put(gk);
260 _leave(" = %d", ret);
261 return ERR_PTR(ret);
262 }
263
264 /*
265 * Use the server secret key to set up the ciphers that will be used to extract
266 * the token from a response packet.
267 */
rxgk_set_up_token_cipher(const struct krb5_buffer * server_key,struct crypto_aead ** token_aead,unsigned int enctype,const struct krb5_enctype ** _krb5,gfp_t gfp)268 int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key,
269 struct crypto_aead **token_aead,
270 unsigned int enctype,
271 const struct krb5_enctype **_krb5,
272 gfp_t gfp)
273 {
274 const struct krb5_enctype *krb5;
275 struct crypto_aead *aead;
276
277 krb5 = crypto_krb5_find_enctype(enctype);
278 if (!krb5)
279 return -ENOPKG;
280
281 aead = crypto_krb5_prepare_encryption(krb5, server_key, RXGK_SERVER_ENC_TOKEN, gfp);
282 if (IS_ERR(aead))
283 return PTR_ERR(aead);
284
285 *_krb5 = krb5;
286 *token_aead = aead;
287 return 0;
288 }
289