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