xref: /netbsd-src/external/mpl/bind/dist/tests/dns/dst_test.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: dst_test.c,v 1.3 2025/01/26 16:25:47 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 #include <inttypes.h>
17 #include <sched.h> /* IWYU pragma: keep */
18 #include <setjmp.h>
19 #include <stdarg.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 
25 /*
26  * As a workaround, include an OpenSSL header file before including cmocka.h,
27  * because OpenSSL 3.1.0 uses __attribute__(malloc), conflicting with a
28  * redefined malloc in cmocka.h.
29  */
30 #include <openssl/err.h>
31 
32 #define UNIT_TESTING
33 #include <cmocka.h>
34 
35 #include <isc/file.h>
36 #include <isc/hex.h>
37 #include <isc/result.h>
38 #include <isc/stdio.h>
39 #include <isc/string.h>
40 #include <isc/util.h>
41 
42 #include <dst/dst.h>
43 
44 #include "dst_internal.h"
45 
46 #include <tests/dns.h>
47 
48 static int
49 setup_test(void **state) {
50 	UNUSED(state);
51 
52 	dst_lib_init(mctx, NULL);
53 
54 	return 0;
55 }
56 
57 static int
58 teardown_test(void **state) {
59 	UNUSED(state);
60 
61 	dst_lib_destroy();
62 
63 	return 0;
64 }
65 
66 /* Read sig in file at path to buf. Check signature ineffability */
67 static isc_result_t
68 sig_fromfile(const char *path, isc_buffer_t *buf) {
69 	isc_result_t result;
70 	size_t rval, len;
71 	FILE *fp = NULL;
72 	unsigned char val;
73 	char *p, *data;
74 	off_t size;
75 
76 	result = isc_stdio_open(path, "rb", &fp);
77 	assert_int_equal(result, ISC_R_SUCCESS);
78 
79 	result = isc_file_getsizefd(fileno(fp), &size);
80 	assert_int_equal(result, ISC_R_SUCCESS);
81 
82 	data = isc_mem_get(mctx, (size + 1));
83 	assert_non_null(data);
84 
85 	len = (size_t)size;
86 	p = data;
87 	while (len != 0U) {
88 		result = isc_stdio_read(p, 1, len, fp, &rval);
89 		assert_int_equal(result, ISC_R_SUCCESS);
90 		len -= rval;
91 		p += rval;
92 	}
93 	isc_stdio_close(fp);
94 
95 	p = data;
96 	len = size;
97 	while (len > 0U) {
98 		if ((*p == '\r') || (*p == '\n')) {
99 			++p;
100 			--len;
101 			continue;
102 		} else if (len < 2U) {
103 			goto err;
104 		}
105 		if (('0' <= *p) && (*p <= '9')) {
106 			val = *p - '0';
107 		} else if (('A' <= *p) && (*p <= 'F')) {
108 			val = *p - 'A' + 10;
109 		} else {
110 			result = ISC_R_BADHEX;
111 			goto err;
112 		}
113 		++p;
114 		val <<= 4;
115 		--len;
116 		if (('0' <= *p) && (*p <= '9')) {
117 			val |= (*p - '0');
118 		} else if (('A' <= *p) && (*p <= 'F')) {
119 			val |= (*p - 'A' + 10);
120 		} else {
121 			result = ISC_R_BADHEX;
122 			goto err;
123 		}
124 		++p;
125 		--len;
126 		isc_buffer_putuint8(buf, val);
127 	}
128 
129 	result = ISC_R_SUCCESS;
130 
131 err:
132 	isc_mem_put(mctx, data, size + 1);
133 	return result;
134 }
135 
136 static void
137 check_sig(const char *datapath, const char *sigpath, const char *keyname,
138 	  dns_keytag_t id, dns_secalg_t alg, int type, bool expect) {
139 	isc_result_t result;
140 	size_t rval, len;
141 	FILE *fp;
142 	dst_key_t *key = NULL;
143 	unsigned char sig[512];
144 	unsigned char *p;
145 	unsigned char *data;
146 	off_t size;
147 	isc_buffer_t b;
148 	isc_buffer_t databuf, sigbuf;
149 	isc_region_t datareg, sigreg;
150 	dns_fixedname_t fname;
151 	dns_name_t *name;
152 	dst_context_t *ctx = NULL;
153 
154 	/*
155 	 * Read data from file in a form usable by dst_verify.
156 	 */
157 	result = isc_stdio_open(datapath, "rb", &fp);
158 	assert_int_equal(result, ISC_R_SUCCESS);
159 
160 	result = isc_file_getsizefd(fileno(fp), &size);
161 	assert_int_equal(result, ISC_R_SUCCESS);
162 
163 	data = isc_mem_get(mctx, (size + 1));
164 	assert_non_null(data);
165 
166 	p = data;
167 	len = (size_t)size;
168 	do {
169 		result = isc_stdio_read(p, 1, len, fp, &rval);
170 		assert_int_equal(result, ISC_R_SUCCESS);
171 		len -= rval;
172 		p += rval;
173 	} while (len);
174 	isc_stdio_close(fp);
175 
176 	/*
177 	 * Read key from file in a form usable by dst_verify.
178 	 */
179 	name = dns_fixedname_initname(&fname);
180 	isc_buffer_constinit(&b, keyname, strlen(keyname));
181 	isc_buffer_add(&b, strlen(keyname));
182 	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
183 	assert_int_equal(result, ISC_R_SUCCESS);
184 	result = dst_key_fromfile(name, id, alg, type,
185 				  TESTS_DIR "/testdata/dst", mctx, &key);
186 	assert_int_equal(result, ISC_R_SUCCESS);
187 
188 	isc_buffer_init(&databuf, data, (unsigned int)size);
189 	isc_buffer_add(&databuf, (unsigned int)size);
190 	isc_buffer_usedregion(&databuf, &datareg);
191 
192 	memset(sig, 0, sizeof(sig));
193 	isc_buffer_init(&sigbuf, sig, sizeof(sig));
194 
195 	/*
196 	 * Read precomputed signature from file in a form usable by dst_verify.
197 	 */
198 	result = sig_fromfile(sigpath, &sigbuf);
199 	assert_int_equal(result, ISC_R_SUCCESS);
200 
201 	/*
202 	 * Verify that the key signed the data.
203 	 */
204 	isc_buffer_remainingregion(&sigbuf, &sigreg);
205 
206 	result = dst_context_create(key, mctx, DNS_LOGCATEGORY_GENERAL, false,
207 				    0, &ctx);
208 	assert_int_equal(result, ISC_R_SUCCESS);
209 
210 	result = dst_context_adddata(ctx, &datareg);
211 	assert_int_equal(result, ISC_R_SUCCESS);
212 	result = dst_context_verify(ctx, &sigreg);
213 
214 	/*
215 	 * Compute the expected signature and emit it
216 	 * so the precomputed signature can be updated.
217 	 * This should only be done if the covered data
218 	 * is updated.
219 	 */
220 	if (expect && result != ISC_R_SUCCESS) {
221 		isc_result_t result2;
222 
223 		dst_context_destroy(&ctx);
224 		result2 = dst_context_create(key, mctx, DNS_LOGCATEGORY_GENERAL,
225 					     false, 0, &ctx);
226 		assert_int_equal(result2, ISC_R_SUCCESS);
227 
228 		result2 = dst_context_adddata(ctx, &datareg);
229 		assert_int_equal(result2, ISC_R_SUCCESS);
230 
231 		char sigbuf2[4096];
232 		isc_buffer_t sigb;
233 		isc_buffer_init(&sigb, sigbuf2, sizeof(sigbuf2));
234 
235 		result2 = dst_context_sign(ctx, &sigb);
236 		assert_int_equal(result2, ISC_R_SUCCESS);
237 
238 		isc_region_t r;
239 		isc_buffer_usedregion(&sigb, &r);
240 
241 		char hexbuf[4096] = { 0 };
242 		isc_buffer_t hb;
243 		isc_buffer_init(&hb, hexbuf, sizeof(hexbuf));
244 
245 		isc_hex_totext(&r, 0, "", &hb);
246 
247 		fprintf(stderr, "# %s:\n# %s\n", sigpath, hexbuf);
248 	}
249 
250 	isc_mem_put(mctx, data, size + 1);
251 	dst_context_destroy(&ctx);
252 	dst_key_free(&key);
253 
254 	assert_true((expect && (result == ISC_R_SUCCESS)) ||
255 		    (!expect && (result != ISC_R_SUCCESS)));
256 
257 	return;
258 }
259 
260 ISC_RUN_TEST_IMPL(sig_test) {
261 	struct {
262 		const char *datapath;
263 		const char *sigpath;
264 		const char *keyname;
265 		dns_keytag_t keyid;
266 		dns_secalg_t alg;
267 		bool expect;
268 	} testcases[] = {
269 		{ TESTS_DIR "/testdata/dst/test1.data",
270 		  TESTS_DIR "/testdata/dst/test1.ecdsa256sig", "test.", 49130,
271 		  DST_ALG_ECDSA256, true },
272 		{ TESTS_DIR "/testdata/dst/test1.data",
273 		  TESTS_DIR "/testdata/dst/test1.rsasha256sig", "test.", 11349,
274 		  DST_ALG_RSASHA256, true },
275 		{ /* wrong sig */
276 		  TESTS_DIR "/testdata/dst/test1.data",
277 		  TESTS_DIR "/testdata/dst/test1.ecdsa256sig", "test.", 11349,
278 		  DST_ALG_RSASHA256, false },
279 		{ /* wrong data */
280 		  TESTS_DIR "/testdata/dst/test2.data",
281 		  TESTS_DIR "/testdata/dst/test1.ecdsa256sig", "test.", 49130,
282 		  DST_ALG_ECDSA256, false },
283 	};
284 	unsigned int i;
285 
286 	for (i = 0; i < (sizeof(testcases) / sizeof(testcases[0])); i++) {
287 		if (!dst_algorithm_supported(testcases[i].alg)) {
288 			continue;
289 		}
290 
291 		check_sig(testcases[i].datapath, testcases[i].sigpath,
292 			  testcases[i].keyname, testcases[i].keyid,
293 			  testcases[i].alg, DST_TYPE_PRIVATE | DST_TYPE_PUBLIC,
294 			  testcases[i].expect);
295 	}
296 }
297 
298 static void
299 check_cmp(const char *key1_name, dns_keytag_t key1_id, const char *key2_name,
300 	  dns_keytag_t key2_id, dns_secalg_t alg, int type, bool expect) {
301 	isc_result_t result;
302 	dst_key_t *key1 = NULL;
303 	dst_key_t *key2 = NULL;
304 	isc_buffer_t b1;
305 	isc_buffer_t b2;
306 	dns_fixedname_t fname1;
307 	dns_fixedname_t fname2;
308 	dns_name_t *name1;
309 	dns_name_t *name2;
310 
311 	/*
312 	 * Read key1 from the file.
313 	 */
314 	name1 = dns_fixedname_initname(&fname1);
315 	isc_buffer_constinit(&b1, key1_name, strlen(key1_name));
316 	isc_buffer_add(&b1, strlen(key1_name));
317 	result = dns_name_fromtext(name1, &b1, dns_rootname, 0, NULL);
318 	assert_int_equal(result, ISC_R_SUCCESS);
319 	result = dst_key_fromfile(name1, key1_id, alg, type,
320 				  TESTS_DIR "/comparekeys", mctx, &key1);
321 	assert_int_equal(result, ISC_R_SUCCESS);
322 
323 	/*
324 	 * Read key2 from the file.
325 	 */
326 	name2 = dns_fixedname_initname(&fname2);
327 	isc_buffer_constinit(&b2, key2_name, strlen(key2_name));
328 	isc_buffer_add(&b2, strlen(key2_name));
329 	result = dns_name_fromtext(name2, &b2, dns_rootname, 0, NULL);
330 	assert_int_equal(result, ISC_R_SUCCESS);
331 	result = dst_key_fromfile(name2, key2_id, alg, type,
332 				  TESTS_DIR "/comparekeys", mctx, &key2);
333 	assert_int_equal(result, ISC_R_SUCCESS);
334 
335 	/*
336 	 * Compare the keys (for public-only keys).
337 	 */
338 	if ((type & DST_TYPE_PRIVATE) == 0) {
339 		assert_true(dst_key_pubcompare(key1, key2, false) == expect);
340 	}
341 
342 	/*
343 	 * Compare the keys (for both public-only keys and keypairs).
344 	 */
345 	assert_true(dst_key_compare(key1, key2) == expect);
346 
347 	/*
348 	 * Free the keys
349 	 */
350 	dst_key_free(&key2);
351 	dst_key_free(&key1);
352 
353 	return;
354 }
355 
356 ISC_RUN_TEST_IMPL(cmp_test) {
357 	struct {
358 		const char *key1_name;
359 		dns_keytag_t key1_id;
360 		const char *key2_name;
361 		dns_keytag_t key2_id;
362 		dns_secalg_t alg;
363 		int type;
364 		bool expect;
365 	} testcases[] = {
366 		/* RSA Keypair: self */
367 		{ "example.", 53461, "example.", 53461, DST_ALG_RSASHA256,
368 		  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, true },
369 
370 		/* RSA Keypair: different key */
371 		{ "example.", 53461, "example2.", 37993, DST_ALG_RSASHA256,
372 		  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, false },
373 
374 		/* RSA Keypair: different PublicExponent (e) */
375 		{ "example.", 53461, "example-e.", 53973, DST_ALG_RSASHA256,
376 		  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, false },
377 
378 		/* RSA Keypair: different Modulus (n) */
379 		{ "example.", 53461, "example-n.", 37464, DST_ALG_RSASHA256,
380 		  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, false },
381 
382 		/* RSA Public Key: self */
383 		{ "example.", 53461, "example.", 53461, DST_ALG_RSASHA256,
384 		  DST_TYPE_PUBLIC, true },
385 
386 		/* RSA Public Key: different key */
387 		{ "example.", 53461, "example2.", 37993, DST_ALG_RSASHA256,
388 		  DST_TYPE_PUBLIC, false },
389 
390 		/* RSA Public Key: different PublicExponent (e) */
391 		{ "example.", 53461, "example-e.", 53973, DST_ALG_RSASHA256,
392 		  DST_TYPE_PUBLIC, false },
393 
394 		/* RSA Public Key: different Modulus (n) */
395 		{ "example.", 53461, "example-n.", 37464, DST_ALG_RSASHA256,
396 		  DST_TYPE_PUBLIC, false },
397 
398 		/* ECDSA Keypair: self */
399 		{ "example.", 19786, "example.", 19786, DST_ALG_ECDSA256,
400 		  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, true },
401 
402 		/* ECDSA Keypair: different key */
403 		{ "example.", 19786, "example2.", 16384, DST_ALG_ECDSA256,
404 		  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, false },
405 
406 		/* ECDSA Public Key: self */
407 		{ "example.", 19786, "example.", 19786, DST_ALG_ECDSA256,
408 		  DST_TYPE_PUBLIC, true },
409 
410 		/* ECDSA Public Key: different key */
411 		{ "example.", 19786, "example2.", 16384, DST_ALG_ECDSA256,
412 		  DST_TYPE_PUBLIC, false },
413 
414 		/* EdDSA Keypair: self */
415 		{ "example.", 63663, "example.", 63663, DST_ALG_ED25519,
416 		  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, true },
417 
418 		/* EdDSA Keypair: different key */
419 		{ "example.", 63663, "example2.", 37529, DST_ALG_ED25519,
420 		  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, false },
421 
422 		/* EdDSA Public Key: self */
423 		{ "example.", 63663, "example.", 63663, DST_ALG_ED25519,
424 		  DST_TYPE_PUBLIC, true },
425 
426 		/* EdDSA Public Key: different key */
427 		{ "example.", 63663, "example2.", 37529, DST_ALG_ED25519,
428 		  DST_TYPE_PUBLIC, false },
429 	};
430 	unsigned int i;
431 
432 	for (i = 0; i < (sizeof(testcases) / sizeof(testcases[0])); i++) {
433 		if (!dst_algorithm_supported(testcases[i].alg)) {
434 			continue;
435 		}
436 
437 		check_cmp(testcases[i].key1_name, testcases[i].key1_id,
438 			  testcases[i].key2_name, testcases[i].key2_id,
439 			  testcases[i].alg, testcases[i].type,
440 			  testcases[i].expect);
441 	}
442 }
443 
444 ISC_TEST_LIST_START
445 ISC_TEST_ENTRY_CUSTOM(sig_test, setup_test, teardown_test)
446 ISC_TEST_ENTRY_CUSTOM(cmp_test, setup_test, teardown_test)
447 ISC_TEST_LIST_END
448 
449 ISC_TEST_MAIN
450