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