Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: dst_test.c,v 1.4 2025/07/17 19:01: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