File Index
Files and directories
Sign in
FILE

selftest.c

File preview with syntax highlighting.

linux-6.19.14/crypto/krb5/selftest.c C
  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
 35enum which_key {
 36	TEST_KC, TEST_KE, TEST_KI,
 37};
 38
 39#if 0
 40static 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
 64static 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
 80static 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	}
101out:
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
107static 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 */
117static 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
153out:
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 */
164static 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
203out:
204	clear_buf(&key);
205	clear_buf(&result);
206	return ret;
207}
208
209static 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
232out:
233	clear_buf(&base_key);
234	return ret;
235}
236
237/*
238 * Perform an encryption test.
239 */
240static 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
363out:
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 */
379static 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
479out:
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
490int 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;
541out:
542	pr_notice("Selftests %s\n", ret == 0 ? "succeeded" : "failed");
543	kfree(buf);
544	return ret;
545}
~/ Read-only file preview.