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(&prf);
156 clear_buf(&octet);
157 clear_buf(&key);
158 return ret;
159 }
160
161 /*
162 * Perform a key derivation check.
163 */
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)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
krb5_test_one_key(const struct krb5_key_test * test)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 */
krb5_test_one_enc(const struct krb5_enc_test * test,void * buf)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 */
krb5_test_one_mic(const struct krb5_mic_test * test,void * buf)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
krb5_selftest(void)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