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 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 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 */ 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 */ 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 */ 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 */ 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