xref: /linux/crypto/krb5/selftest.c (revision 989fe6771266bdb82a815d78802c5aa7c918fdfd)
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(&prf);
156 	clear_buf(&octet);
157 	clear_buf(&key);
158 	return ret;
159 }
160 
161 /*
162  * Perform a key derivation check.
163  */
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 
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  */
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  */
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 
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