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
prep_buf(struct krb5_buffer * buf)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
load_buf(struct krb5_buffer * buf,const char * from)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
clear_buf(struct krb5_buffer * buf)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 */
krb5_test_one_prf(const struct krb5_prf_test * test)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 */
krb5_test_key(const struct krb5_enctype * krb5,const struct krb5_buffer * base_key,const struct krb5_key_test_one * test,enum which_key which)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
krb5_test_one_key(const struct krb5_key_test * test)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 */
krb5_test_one_enc(const struct krb5_enc_test * test,void * buf)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 */
krb5_test_one_mic(const struct krb5_mic_test * test,void * buf)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
krb5_selftest(void)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