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