File Index
Files and directories
Sign in
FILE

rxgk_kdf.c

File preview with syntax highlighting.

linux-6.19.14/net/rxrpc/rxgk_kdf.c C
  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
 28static 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
 43void 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 */
 56static 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 */
 95static 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;
190out:
191	kfree_sensitive(buffer);
192	return ret;
193aead_error:
194	ret = PTR_ERR(aead);
195	goto out;
196hash_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 */
205struct 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
258err_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 */
268int 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}
~/ Read-only file preview.