1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* Kerberos library self-testing 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 <crypto/skcipher.h> 12 #include <crypto/hash.h> 13 #include "internal.h" 14 15 #define VALID(X) \ 16 ({ \ 17 bool __x = (X); \ 18 if (__x) { \ 19 pr_warn("!!! TESTINVAL %s:%u\n", __FILE__, __LINE__); \ 20 ret = -EBADMSG; \ 21 } \ 22 __x; \ 23 }) 24 25 #define CHECK(X) \ 26 ({ \ 27 bool __x = (X); \ 28 if (__x) { \ 29 pr_warn("!!! TESTFAIL %s:%u\n", __FILE__, __LINE__); \ 30 ret = -EBADMSG; \ 31 } \ 32 __x; \ 33 }) 34 35 enum which_key { 36 TEST_KC, TEST_KE, TEST_KI, 37 }; 38 39 #if 0 40 static void dump_sg(struct scatterlist *sg, unsigned int limit) 41 { 42 unsigned int index = 0, n = 0; 43 44 for (; sg && limit > 0; sg = sg_next(sg)) { 45 unsigned int off = sg->offset, len = umin(sg->length, limit); 46 const void *p = kmap_local_page(sg_page(sg)); 47 48 limit -= len; 49 while (len > 0) { 50 unsigned int part = umin(len, 32); 51 52 pr_notice("[%x] %04x: %*phN\n", n, index, part, p + off); 53 index += part; 54 off += part; 55 len -= part; 56 } 57 58 kunmap_local(p); 59 n++; 60 } 61 } 62 #endif 63 64 static int prep_buf(struct krb5_buffer *buf) 65 { 66 buf->data = kmalloc(buf->len, GFP_KERNEL); 67 if (!buf->data) 68 return -ENOMEM; 69 return 0; 70 } 71 72 #define PREP_BUF(BUF, LEN) \ 73 do { \ 74 (BUF)->len = (LEN); \ 75 ret = prep_buf((BUF)); \ 76 if (ret < 0) \ 77 goto out; \ 78 } while (0) 79 80 static int load_buf(struct krb5_buffer *buf, const char *from) 81 { 82 size_t len = strlen(from); 83 int ret; 84 85 if (len > 1 && from[0] == '\'') { 86 PREP_BUF(buf, len - 1); 87 memcpy(buf->data, from + 1, len - 1); 88 ret = 0; 89 goto out; 90 } 91 92 if (VALID(len & 1)) 93 return -EINVAL; 94 95 PREP_BUF(buf, len / 2); 96 ret = hex2bin(buf->data, from, buf->len); 97 if (ret < 0) { 98 VALID(1); 99 goto out; 100 } 101 out: 102 return ret; 103 } 104 105 #define LOAD_BUF(BUF, FROM) do { ret = load_buf(BUF, FROM); if (ret < 0) goto out; } while (0) 106 107 static void clear_buf(struct krb5_buffer *buf) 108 { 109 kfree(buf->data); 110 buf->len = 0; 111 buf->data = NULL; 112 } 113 114 /* 115 * Perform a pseudo-random function check. 116 */ 117 static int krb5_test_one_prf(const struct krb5_prf_test *test) 118 { 119 const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); 120 struct krb5_buffer key = {}, octet = {}, result = {}, prf = {}; 121 int ret; 122 123 if (!krb5) 124 return -EOPNOTSUPP; 125 126 pr_notice("Running %s %s\n", krb5->name, test->name); 127 128 LOAD_BUF(&key, test->key); 129 LOAD_BUF(&octet, test->octet); 130 LOAD_BUF(&prf, test->prf); 131 PREP_BUF(&result, krb5->prf_len); 132 133 if (VALID(result.len != prf.len)) { 134 ret = -EINVAL; 135 goto out; 136 } 137 138 ret = krb5->profile->calc_PRF(krb5, &key, &octet, &result, GFP_KERNEL); 139 if (ret < 0) { 140 CHECK(1); 141 pr_warn("PRF calculation failed %d\n", ret); 142 goto out; 143 } 144 145 if (memcmp(result.data, prf.data, result.len) != 0) { 146 CHECK(1); 147 ret = -EKEYREJECTED; 148 goto out; 149 } 150 151 ret = 0; 152 153 out: 154 clear_buf(&result); 155 clear_buf(&prf); 156 clear_buf(&octet); 157 clear_buf(&key); 158 return ret; 159 } 160 161 /* 162 * Perform a key derivation check. 163 */ 164 static int krb5_test_key(const struct krb5_enctype *krb5, 165 const struct krb5_buffer *base_key, 166 const struct krb5_key_test_one *test, 167 enum which_key which) 168 { 169 struct krb5_buffer key = {}, result = {}; 170 int ret; 171 172 LOAD_BUF(&key, test->key); 173 PREP_BUF(&result, key.len); 174 175 switch (which) { 176 case TEST_KC: 177 ret = krb5_derive_Kc(krb5, base_key, test->use, &result, GFP_KERNEL); 178 break; 179 case TEST_KE: 180 ret = krb5_derive_Ke(krb5, base_key, test->use, &result, GFP_KERNEL); 181 break; 182 case TEST_KI: 183 ret = krb5_derive_Ki(krb5, base_key, test->use, &result, GFP_KERNEL); 184 break; 185 default: 186 VALID(1); 187 ret = -EINVAL; 188 goto out; 189 } 190 191 if (ret < 0) { 192 CHECK(1); 193 pr_warn("Key derivation failed %d\n", ret); 194 goto out; 195 } 196 197 if (memcmp(result.data, key.data, result.len) != 0) { 198 CHECK(1); 199 ret = -EKEYREJECTED; 200 goto out; 201 } 202 203 out: 204 clear_buf(&key); 205 clear_buf(&result); 206 return ret; 207 } 208 209 static int krb5_test_one_key(const struct krb5_key_test *test) 210 { 211 const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); 212 struct krb5_buffer base_key = {}; 213 int ret; 214 215 if (!krb5) 216 return -EOPNOTSUPP; 217 218 pr_notice("Running %s %s\n", krb5->name, test->name); 219 220 LOAD_BUF(&base_key, test->key); 221 222 ret = krb5_test_key(krb5, &base_key, &test->Kc, TEST_KC); 223 if (ret < 0) 224 goto out; 225 ret = krb5_test_key(krb5, &base_key, &test->Ke, TEST_KE); 226 if (ret < 0) 227 goto out; 228 ret = krb5_test_key(krb5, &base_key, &test->Ki, TEST_KI); 229 if (ret < 0) 230 goto out; 231 232 out: 233 clear_buf(&base_key); 234 return ret; 235 } 236 237 /* 238 * Perform an encryption test. 239 */ 240 static int krb5_test_one_enc(const struct krb5_enc_test *test, void *buf) 241 { 242 const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); 243 struct crypto_aead *ci = NULL; 244 struct krb5_buffer K0 = {}, Ke = {}, Ki = {}, keys = {}; 245 struct krb5_buffer conf = {}, plain = {}, ct = {}; 246 struct scatterlist sg[1]; 247 size_t data_len, data_offset, message_len; 248 int ret; 249 250 if (!krb5) 251 return -EOPNOTSUPP; 252 253 pr_notice("Running %s %s\n", krb5->name, test->name); 254 255 /* Load the test data into binary buffers. */ 256 LOAD_BUF(&conf, test->conf); 257 LOAD_BUF(&plain, test->plain); 258 LOAD_BUF(&ct, test->ct); 259 260 if (test->K0) { 261 LOAD_BUF(&K0, test->K0); 262 } else { 263 LOAD_BUF(&Ke, test->Ke); 264 LOAD_BUF(&Ki, test->Ki); 265 266 ret = krb5->profile->load_encrypt_keys(krb5, &Ke, &Ki, &keys, GFP_KERNEL); 267 if (ret < 0) 268 goto out; 269 } 270 271 if (VALID(conf.len != krb5->conf_len) || 272 VALID(ct.len != krb5->conf_len + plain.len + krb5->cksum_len)) 273 goto out; 274 275 data_len = plain.len; 276 message_len = crypto_krb5_how_much_buffer(krb5, KRB5_ENCRYPT_MODE, 277 data_len, &data_offset); 278 279 if (CHECK(message_len != ct.len)) { 280 pr_warn("Encrypted length mismatch %zu != %u\n", message_len, ct.len); 281 goto out; 282 } 283 if (CHECK(data_offset != conf.len)) { 284 pr_warn("Data offset mismatch %zu != %u\n", data_offset, conf.len); 285 goto out; 286 } 287 288 memcpy(buf, conf.data, conf.len); 289 memcpy(buf + data_offset, plain.data, plain.len); 290 291 /* Allocate a crypto object and set its key. */ 292 if (test->K0) 293 ci = crypto_krb5_prepare_encryption(krb5, &K0, test->usage, GFP_KERNEL); 294 else 295 ci = krb5_prepare_encryption(krb5, &keys, GFP_KERNEL); 296 297 if (IS_ERR(ci)) { 298 ret = PTR_ERR(ci); 299 ci = NULL; 300 pr_err("Couldn't alloc AEAD %s: %d\n", krb5->encrypt_name, ret); 301 goto out; 302 } 303 304 /* Encrypt the message. */ 305 sg_init_one(sg, buf, message_len); 306 ret = crypto_krb5_encrypt(krb5, ci, sg, 1, message_len, 307 data_offset, data_len, true); 308 if (ret < 0) { 309 CHECK(1); 310 pr_warn("Encryption failed %d\n", ret); 311 goto out; 312 } 313 if (ret != message_len) { 314 CHECK(1); 315 pr_warn("Encrypted message wrong size %x != %zx\n", ret, message_len); 316 goto out; 317 } 318 319 if (memcmp(buf, ct.data, ct.len) != 0) { 320 CHECK(1); 321 pr_warn("Ciphertext mismatch\n"); 322 pr_warn("BUF %*phN\n", ct.len, buf); 323 pr_warn("CT %*phN\n", ct.len, ct.data); 324 pr_warn("PT %*phN%*phN\n", conf.len, conf.data, plain.len, plain.data); 325 ret = -EKEYREJECTED; 326 goto out; 327 } 328 329 /* Decrypt the encrypted message. */ 330 data_offset = 0; 331 data_len = message_len; 332 ret = crypto_krb5_decrypt(krb5, ci, sg, 1, &data_offset, &data_len); 333 if (ret < 0) { 334 CHECK(1); 335 pr_warn("Decryption failed %d\n", ret); 336 goto out; 337 } 338 339 if (CHECK(data_offset != conf.len) || 340 CHECK(data_len != plain.len)) 341 goto out; 342 343 if (memcmp(buf, conf.data, conf.len) != 0) { 344 CHECK(1); 345 pr_warn("Confounder mismatch\n"); 346 pr_warn("ENC %*phN\n", conf.len, buf); 347 pr_warn("DEC %*phN\n", conf.len, conf.data); 348 ret = -EKEYREJECTED; 349 goto out; 350 } 351 352 if (memcmp(buf + conf.len, plain.data, plain.len) != 0) { 353 CHECK(1); 354 pr_warn("Plaintext mismatch\n"); 355 pr_warn("BUF %*phN\n", plain.len, buf + conf.len); 356 pr_warn("PT %*phN\n", plain.len, plain.data); 357 ret = -EKEYREJECTED; 358 goto out; 359 } 360 361 ret = 0; 362 363 out: 364 clear_buf(&ct); 365 clear_buf(&plain); 366 clear_buf(&conf); 367 clear_buf(&keys); 368 clear_buf(&Ki); 369 clear_buf(&Ke); 370 clear_buf(&K0); 371 if (ci) 372 crypto_free_aead(ci); 373 return ret; 374 } 375 376 /* 377 * Perform a checksum test. 378 */ 379 static int krb5_test_one_mic(const struct krb5_mic_test *test, void *buf) 380 { 381 const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); 382 struct crypto_shash *ci = NULL; 383 struct scatterlist sg[1]; 384 struct krb5_buffer K0 = {}, Kc = {}, keys = {}, plain = {}, mic = {}; 385 size_t offset, len, message_len; 386 int ret; 387 388 if (!krb5) 389 return -EOPNOTSUPP; 390 391 pr_notice("Running %s %s\n", krb5->name, test->name); 392 393 /* Allocate a crypto object and set its key. */ 394 if (test->K0) { 395 LOAD_BUF(&K0, test->K0); 396 ci = crypto_krb5_prepare_checksum(krb5, &K0, test->usage, GFP_KERNEL); 397 } else { 398 LOAD_BUF(&Kc, test->Kc); 399 400 ret = krb5->profile->load_checksum_key(krb5, &Kc, &keys, GFP_KERNEL); 401 if (ret < 0) 402 goto out; 403 404 ci = krb5_prepare_checksum(krb5, &Kc, GFP_KERNEL); 405 } 406 if (IS_ERR(ci)) { 407 ret = PTR_ERR(ci); 408 ci = NULL; 409 pr_err("Couldn't alloc shash %s: %d\n", krb5->cksum_name, ret); 410 goto out; 411 } 412 413 /* Load the test data into binary buffers. */ 414 LOAD_BUF(&plain, test->plain); 415 LOAD_BUF(&mic, test->mic); 416 417 len = plain.len; 418 message_len = crypto_krb5_how_much_buffer(krb5, KRB5_CHECKSUM_MODE, 419 len, &offset); 420 421 if (CHECK(message_len != mic.len + plain.len)) { 422 pr_warn("MIC length mismatch %zu != %u\n", 423 message_len, mic.len + plain.len); 424 goto out; 425 } 426 427 memcpy(buf + offset, plain.data, plain.len); 428 429 /* Generate a MIC generation request. */ 430 sg_init_one(sg, buf, 1024); 431 432 ret = crypto_krb5_get_mic(krb5, ci, NULL, sg, 1, 1024, 433 krb5->cksum_len, plain.len); 434 if (ret < 0) { 435 CHECK(1); 436 pr_warn("Get MIC failed %d\n", ret); 437 goto out; 438 } 439 len = ret; 440 441 if (CHECK(len != plain.len + mic.len)) { 442 pr_warn("MIC length mismatch %zu != %u\n", len, plain.len + mic.len); 443 goto out; 444 } 445 446 if (memcmp(buf, mic.data, mic.len) != 0) { 447 CHECK(1); 448 pr_warn("MIC mismatch\n"); 449 pr_warn("BUF %*phN\n", mic.len, buf); 450 pr_warn("MIC %*phN\n", mic.len, mic.data); 451 ret = -EKEYREJECTED; 452 goto out; 453 } 454 455 /* Generate a verification request. */ 456 offset = 0; 457 ret = crypto_krb5_verify_mic(krb5, ci, NULL, sg, 1, &offset, &len); 458 if (ret < 0) { 459 CHECK(1); 460 pr_warn("Verify MIC failed %d\n", ret); 461 goto out; 462 } 463 464 if (CHECK(offset != mic.len) || 465 CHECK(len != plain.len)) 466 goto out; 467 468 if (memcmp(buf + offset, plain.data, plain.len) != 0) { 469 CHECK(1); 470 pr_warn("Plaintext mismatch\n"); 471 pr_warn("BUF %*phN\n", plain.len, buf + offset); 472 pr_warn("PT %*phN\n", plain.len, plain.data); 473 ret = -EKEYREJECTED; 474 goto out; 475 } 476 477 ret = 0; 478 479 out: 480 clear_buf(&mic); 481 clear_buf(&plain); 482 clear_buf(&keys); 483 clear_buf(&K0); 484 clear_buf(&Kc); 485 if (ci) 486 crypto_free_shash(ci); 487 return ret; 488 } 489 490 int krb5_selftest(void) 491 { 492 void *buf; 493 int ret = 0, i; 494 495 buf = kmalloc(4096, GFP_KERNEL); 496 if (!buf) 497 return -ENOMEM; 498 499 pr_notice("\n"); 500 pr_notice("Running selftests\n"); 501 502 for (i = 0; krb5_prf_tests[i].name; i++) { 503 ret = krb5_test_one_prf(&krb5_prf_tests[i]); 504 if (ret < 0) { 505 if (ret != -EOPNOTSUPP) 506 goto out; 507 pr_notice("Skipping %s\n", krb5_prf_tests[i].name); 508 } 509 } 510 511 for (i = 0; krb5_key_tests[i].name; i++) { 512 ret = krb5_test_one_key(&krb5_key_tests[i]); 513 if (ret < 0) { 514 if (ret != -EOPNOTSUPP) 515 goto out; 516 pr_notice("Skipping %s\n", krb5_key_tests[i].name); 517 } 518 } 519 520 for (i = 0; krb5_enc_tests[i].name; i++) { 521 memset(buf, 0x5a, 4096); 522 ret = krb5_test_one_enc(&krb5_enc_tests[i], buf); 523 if (ret < 0) { 524 if (ret != -EOPNOTSUPP) 525 goto out; 526 pr_notice("Skipping %s\n", krb5_enc_tests[i].name); 527 } 528 } 529 530 for (i = 0; krb5_mic_tests[i].name; i++) { 531 memset(buf, 0x5a, 4096); 532 ret = krb5_test_one_mic(&krb5_mic_tests[i], buf); 533 if (ret < 0) { 534 if (ret != -EOPNOTSUPP) 535 goto out; 536 pr_notice("Skipping %s\n", krb5_mic_tests[i].name); 537 } 538 } 539 540 ret = 0; 541 out: 542 pr_notice("Selftests %s\n", ret == 0 ? "succeeded" : "failed"); 543 kfree(buf); 544 return ret; 545 } 546