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(&octet); 156 clear_buf(&key); 157 return ret; 158 } 159 160 /* 161 * Perform a key derivation check. 162 */ 163 static int krb5_test_key(const struct krb5_enctype *krb5, 164 const struct krb5_buffer *base_key, 165 const struct krb5_key_test_one *test, 166 enum which_key which) 167 { 168 struct krb5_buffer key = {}, result = {}; 169 int ret; 170 171 LOAD_BUF(&key, test->key); 172 PREP_BUF(&result, key.len); 173 174 switch (which) { 175 case TEST_KC: 176 ret = krb5_derive_Kc(krb5, base_key, test->use, &result, GFP_KERNEL); 177 break; 178 case TEST_KE: 179 ret = krb5_derive_Ke(krb5, base_key, test->use, &result, GFP_KERNEL); 180 break; 181 case TEST_KI: 182 ret = krb5_derive_Ki(krb5, base_key, test->use, &result, GFP_KERNEL); 183 break; 184 default: 185 VALID(1); 186 ret = -EINVAL; 187 goto out; 188 } 189 190 if (ret < 0) { 191 CHECK(1); 192 pr_warn("Key derivation failed %d\n", ret); 193 goto out; 194 } 195 196 if (memcmp(result.data, key.data, result.len) != 0) { 197 CHECK(1); 198 ret = -EKEYREJECTED; 199 goto out; 200 } 201 202 out: 203 clear_buf(&key); 204 clear_buf(&result); 205 return ret; 206 } 207 208 static int krb5_test_one_key(const struct krb5_key_test *test) 209 { 210 const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); 211 struct krb5_buffer base_key = {}; 212 int ret; 213 214 if (!krb5) 215 return -EOPNOTSUPP; 216 217 pr_notice("Running %s %s\n", krb5->name, test->name); 218 219 LOAD_BUF(&base_key, test->key); 220 221 ret = krb5_test_key(krb5, &base_key, &test->Kc, TEST_KC); 222 if (ret < 0) 223 goto out; 224 ret = krb5_test_key(krb5, &base_key, &test->Ke, TEST_KE); 225 if (ret < 0) 226 goto out; 227 ret = krb5_test_key(krb5, &base_key, &test->Ki, TEST_KI); 228 if (ret < 0) 229 goto out; 230 231 out: 232 clear_buf(&base_key); 233 return ret; 234 } 235 236 /* 237 * Perform an encryption test. 238 */ 239 static int krb5_test_one_enc(const struct krb5_enc_test *test, void *buf) 240 { 241 const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); 242 struct crypto_aead *ci = NULL; 243 struct krb5_buffer K0 = {}, Ke = {}, Ki = {}, keys = {}; 244 struct krb5_buffer conf = {}, plain = {}, ct = {}; 245 struct scatterlist sg[1]; 246 size_t data_len, data_offset, message_len; 247 int ret; 248 249 if (!krb5) 250 return -EOPNOTSUPP; 251 252 pr_notice("Running %s %s\n", krb5->name, test->name); 253 254 /* Load the test data into binary buffers. */ 255 LOAD_BUF(&conf, test->conf); 256 LOAD_BUF(&plain, test->plain); 257 LOAD_BUF(&ct, test->ct); 258 259 if (test->K0) { 260 LOAD_BUF(&K0, test->K0); 261 } else { 262 LOAD_BUF(&Ke, test->Ke); 263 LOAD_BUF(&Ki, test->Ki); 264 265 ret = krb5->profile->load_encrypt_keys(krb5, &Ke, &Ki, &keys, GFP_KERNEL); 266 if (ret < 0) 267 goto out; 268 } 269 270 if (VALID(conf.len != krb5->conf_len) || 271 VALID(ct.len != krb5->conf_len + plain.len + krb5->cksum_len)) 272 goto out; 273 274 data_len = plain.len; 275 message_len = crypto_krb5_how_much_buffer(krb5, KRB5_ENCRYPT_MODE, 276 data_len, &data_offset); 277 278 if (CHECK(message_len != ct.len)) { 279 pr_warn("Encrypted length mismatch %zu != %u\n", message_len, ct.len); 280 goto out; 281 } 282 if (CHECK(data_offset != conf.len)) { 283 pr_warn("Data offset mismatch %zu != %u\n", data_offset, conf.len); 284 goto out; 285 } 286 287 memcpy(buf, conf.data, conf.len); 288 memcpy(buf + data_offset, plain.data, plain.len); 289 290 /* Allocate a crypto object and set its key. */ 291 if (test->K0) 292 ci = crypto_krb5_prepare_encryption(krb5, &K0, test->usage, GFP_KERNEL); 293 else 294 ci = krb5_prepare_encryption(krb5, &keys, GFP_KERNEL); 295 296 if (IS_ERR(ci)) { 297 ret = PTR_ERR(ci); 298 ci = NULL; 299 pr_err("Couldn't alloc AEAD %s: %d\n", krb5->encrypt_name, ret); 300 goto out; 301 } 302 303 /* Encrypt the message. */ 304 sg_init_one(sg, buf, message_len); 305 ret = crypto_krb5_encrypt(krb5, ci, sg, 1, message_len, 306 data_offset, data_len, true); 307 if (ret < 0) { 308 CHECK(1); 309 pr_warn("Encryption failed %d\n", ret); 310 goto out; 311 } 312 if (ret != message_len) { 313 CHECK(1); 314 pr_warn("Encrypted message wrong size %x != %zx\n", ret, message_len); 315 goto out; 316 } 317 318 if (memcmp(buf, ct.data, ct.len) != 0) { 319 CHECK(1); 320 pr_warn("Ciphertext mismatch\n"); 321 pr_warn("BUF %*phN\n", ct.len, buf); 322 pr_warn("CT %*phN\n", ct.len, ct.data); 323 pr_warn("PT %*phN%*phN\n", conf.len, conf.data, plain.len, plain.data); 324 ret = -EKEYREJECTED; 325 goto out; 326 } 327 328 /* Decrypt the encrypted message. */ 329 data_offset = 0; 330 data_len = message_len; 331 ret = crypto_krb5_decrypt(krb5, ci, sg, 1, &data_offset, &data_len); 332 if (ret < 0) { 333 CHECK(1); 334 pr_warn("Decryption failed %d\n", ret); 335 goto out; 336 } 337 338 if (CHECK(data_offset != conf.len) || 339 CHECK(data_len != plain.len)) 340 goto out; 341 342 if (memcmp(buf, conf.data, conf.len) != 0) { 343 CHECK(1); 344 pr_warn("Confounder mismatch\n"); 345 pr_warn("ENC %*phN\n", conf.len, buf); 346 pr_warn("DEC %*phN\n", conf.len, conf.data); 347 ret = -EKEYREJECTED; 348 goto out; 349 } 350 351 if (memcmp(buf + conf.len, plain.data, plain.len) != 0) { 352 CHECK(1); 353 pr_warn("Plaintext mismatch\n"); 354 pr_warn("BUF %*phN\n", plain.len, buf + conf.len); 355 pr_warn("PT %*phN\n", plain.len, plain.data); 356 ret = -EKEYREJECTED; 357 goto out; 358 } 359 360 ret = 0; 361 362 out: 363 clear_buf(&ct); 364 clear_buf(&plain); 365 clear_buf(&conf); 366 clear_buf(&keys); 367 clear_buf(&Ki); 368 clear_buf(&Ke); 369 clear_buf(&K0); 370 if (ci) 371 crypto_free_aead(ci); 372 return ret; 373 } 374 375 /* 376 * Perform a checksum test. 377 */ 378 static int krb5_test_one_mic(const struct krb5_mic_test *test, void *buf) 379 { 380 const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); 381 struct crypto_shash *ci = NULL; 382 struct scatterlist sg[1]; 383 struct krb5_buffer K0 = {}, Kc = {}, keys = {}, plain = {}, mic = {}; 384 size_t offset, len, message_len; 385 int ret; 386 387 if (!krb5) 388 return -EOPNOTSUPP; 389 390 pr_notice("Running %s %s\n", krb5->name, test->name); 391 392 /* Allocate a crypto object and set its key. */ 393 if (test->K0) { 394 LOAD_BUF(&K0, test->K0); 395 ci = crypto_krb5_prepare_checksum(krb5, &K0, test->usage, GFP_KERNEL); 396 } else { 397 LOAD_BUF(&Kc, test->Kc); 398 399 ret = krb5->profile->load_checksum_key(krb5, &Kc, &keys, GFP_KERNEL); 400 if (ret < 0) 401 goto out; 402 403 ci = krb5_prepare_checksum(krb5, &Kc, GFP_KERNEL); 404 } 405 if (IS_ERR(ci)) { 406 ret = PTR_ERR(ci); 407 ci = NULL; 408 pr_err("Couldn't alloc shash %s: %d\n", krb5->cksum_name, ret); 409 goto out; 410 } 411 412 /* Load the test data into binary buffers. */ 413 LOAD_BUF(&plain, test->plain); 414 LOAD_BUF(&mic, test->mic); 415 416 len = plain.len; 417 message_len = crypto_krb5_how_much_buffer(krb5, KRB5_CHECKSUM_MODE, 418 len, &offset); 419 420 if (CHECK(message_len != mic.len + plain.len)) { 421 pr_warn("MIC length mismatch %zu != %u\n", 422 message_len, mic.len + plain.len); 423 goto out; 424 } 425 426 memcpy(buf + offset, plain.data, plain.len); 427 428 /* Generate a MIC generation request. */ 429 sg_init_one(sg, buf, 1024); 430 431 ret = crypto_krb5_get_mic(krb5, ci, NULL, sg, 1, 1024, 432 krb5->cksum_len, plain.len); 433 if (ret < 0) { 434 CHECK(1); 435 pr_warn("Get MIC failed %d\n", ret); 436 goto out; 437 } 438 len = ret; 439 440 if (CHECK(len != plain.len + mic.len)) { 441 pr_warn("MIC length mismatch %zu != %u\n", len, plain.len + mic.len); 442 goto out; 443 } 444 445 if (memcmp(buf, mic.data, mic.len) != 0) { 446 CHECK(1); 447 pr_warn("MIC mismatch\n"); 448 pr_warn("BUF %*phN\n", mic.len, buf); 449 pr_warn("MIC %*phN\n", mic.len, mic.data); 450 ret = -EKEYREJECTED; 451 goto out; 452 } 453 454 /* Generate a verification request. */ 455 offset = 0; 456 ret = crypto_krb5_verify_mic(krb5, ci, NULL, sg, 1, &offset, &len); 457 if (ret < 0) { 458 CHECK(1); 459 pr_warn("Verify MIC failed %d\n", ret); 460 goto out; 461 } 462 463 if (CHECK(offset != mic.len) || 464 CHECK(len != plain.len)) 465 goto out; 466 467 if (memcmp(buf + offset, plain.data, plain.len) != 0) { 468 CHECK(1); 469 pr_warn("Plaintext mismatch\n"); 470 pr_warn("BUF %*phN\n", plain.len, buf + offset); 471 pr_warn("PT %*phN\n", plain.len, plain.data); 472 ret = -EKEYREJECTED; 473 goto out; 474 } 475 476 ret = 0; 477 478 out: 479 clear_buf(&mic); 480 clear_buf(&plain); 481 clear_buf(&keys); 482 clear_buf(&K0); 483 clear_buf(&Kc); 484 if (ci) 485 crypto_free_shash(ci); 486 return ret; 487 } 488 489 int krb5_selftest(void) 490 { 491 void *buf; 492 int ret = 0, i; 493 494 buf = kmalloc(4096, GFP_KERNEL); 495 if (!buf) 496 return -ENOMEM; 497 498 pr_notice("\n"); 499 pr_notice("Running selftests\n"); 500 501 for (i = 0; krb5_prf_tests[i].name; i++) { 502 ret = krb5_test_one_prf(&krb5_prf_tests[i]); 503 if (ret < 0) { 504 if (ret != -EOPNOTSUPP) 505 goto out; 506 pr_notice("Skipping %s\n", krb5_prf_tests[i].name); 507 } 508 } 509 510 for (i = 0; krb5_key_tests[i].name; i++) { 511 ret = krb5_test_one_key(&krb5_key_tests[i]); 512 if (ret < 0) { 513 if (ret != -EOPNOTSUPP) 514 goto out; 515 pr_notice("Skipping %s\n", krb5_key_tests[i].name); 516 } 517 } 518 519 for (i = 0; krb5_enc_tests[i].name; i++) { 520 memset(buf, 0x5a, 4096); 521 ret = krb5_test_one_enc(&krb5_enc_tests[i], buf); 522 if (ret < 0) { 523 if (ret != -EOPNOTSUPP) 524 goto out; 525 pr_notice("Skipping %s\n", krb5_enc_tests[i].name); 526 } 527 } 528 529 for (i = 0; krb5_mic_tests[i].name; i++) { 530 memset(buf, 0x5a, 4096); 531 ret = krb5_test_one_mic(&krb5_mic_tests[i], buf); 532 if (ret < 0) { 533 if (ret != -EOPNOTSUPP) 534 goto out; 535 pr_notice("Skipping %s\n", krb5_mic_tests[i].name); 536 } 537 } 538 539 ret = 0; 540 out: 541 pr_notice("Selftests %s\n", ret == 0 ? "succeeded" : "failed"); 542 kfree(buf); 543 return ret; 544 } 545