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