1/* $NetBSD: dst_test.c,v 1.2 2024/02/21 22:52:50 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 48static int 49setup_test(void **state) { 50 UNUSED(state); 51 52 dst_lib_init(mctx, NULL); 53 54 return (0); 55} 56 57static int 58teardown_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 */ 67static isc_result_t 68sig_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 131err: 132 isc_mem_put(mctx, data, size + 1); 133 return (result); 134} 135 136static void 137check_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 260ISC_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 298static void 299check_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 356ISC_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 Keypair: different PrivateExponent (d) */ 383 { "example.", 53461, "example-d.", 53461, DST_ALG_RSASHA256, 384 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, false }, 385 386 /* RSA Keypair: different Prime1 (p) */ 387 { "example.", 53461, "example-p.", 53461, DST_ALG_RSASHA256, 388 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, false }, 389 390 /* RSA Keypair: different Prime2 (q) */ 391 { "example.", 53461, "example-q.", 53461, DST_ALG_RSASHA256, 392 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, false }, 393 394 /* RSA Public Key: self */ 395 { "example.", 53461, "example.", 53461, DST_ALG_RSASHA256, 396 DST_TYPE_PUBLIC, true }, 397 398 /* RSA Public Key: different key */ 399 { "example.", 53461, "example2.", 37993, DST_ALG_RSASHA256, 400 DST_TYPE_PUBLIC, false }, 401 402 /* RSA Public Key: different PublicExponent (e) */ 403 { "example.", 53461, "example-e.", 53973, DST_ALG_RSASHA256, 404 DST_TYPE_PUBLIC, false }, 405 406 /* RSA Public Key: different Modulus (n) */ 407 { "example.", 53461, "example-n.", 37464, DST_ALG_RSASHA256, 408 DST_TYPE_PUBLIC, false }, 409 410 /* ECDSA Keypair: self */ 411 { "example.", 19786, "example.", 19786, DST_ALG_ECDSA256, 412 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, true }, 413 414 /* ECDSA Keypair: different key */ 415 { "example.", 19786, "example2.", 16384, DST_ALG_ECDSA256, 416 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, false }, 417 418 /* ECDSA Public Key: self */ 419 { "example.", 19786, "example.", 19786, DST_ALG_ECDSA256, 420 DST_TYPE_PUBLIC, true }, 421 422 /* ECDSA Public Key: different key */ 423 { "example.", 19786, "example2.", 16384, DST_ALG_ECDSA256, 424 DST_TYPE_PUBLIC, false }, 425 426 /* EdDSA Keypair: self */ 427 { "example.", 63663, "example.", 63663, DST_ALG_ED25519, 428 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, true }, 429 430 /* EdDSA Keypair: different key */ 431 { "example.", 63663, "example2.", 37529, DST_ALG_ED25519, 432 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, false }, 433 434 /* EdDSA Public Key: self */ 435 { "example.", 63663, "example.", 63663, DST_ALG_ED25519, 436 DST_TYPE_PUBLIC, true }, 437 438 /* EdDSA Public Key: different key */ 439 { "example.", 63663, "example2.", 37529, DST_ALG_ED25519, 440 DST_TYPE_PUBLIC, false }, 441 442 /* DH Keypair: self */ 443 { "example.", 65316, "example.", 65316, DST_ALG_DH, 444 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_KEY, true }, 445 446 /* DH Keypair: different key */ 447 { "example.", 65316, "example2.", 19823, DST_ALG_DH, 448 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_KEY, false }, 449 450 /* DH Keypair: different key (with generator=5) */ 451 { "example.", 65316, "example3.", 17187, DST_ALG_DH, 452 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_KEY, false }, 453 454 /* DH Keypair: different private key */ 455 { "example.", 65316, "example-private.", 65316, DST_ALG_DH, 456 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_KEY, false }, 457 458 /* DH Public Key: self */ 459 { "example.", 65316, "example.", 65316, DST_ALG_DH, 460 DST_TYPE_PUBLIC | DST_TYPE_KEY, true }, 461 462 /* DH Public Key: different key */ 463 { "example.", 65316, "example2.", 19823, DST_ALG_DH, 464 DST_TYPE_PUBLIC | DST_TYPE_KEY, false }, 465 466 /* DH Public Key: different key (with generator=5) */ 467 { "example.", 65316, "example3.", 17187, DST_ALG_DH, 468 DST_TYPE_PUBLIC | DST_TYPE_KEY, false }, 469 }; 470 unsigned int i; 471 472 for (i = 0; i < (sizeof(testcases) / sizeof(testcases[0])); i++) { 473 if (!dst_algorithm_supported(testcases[i].alg)) { 474 continue; 475 } 476 477 check_cmp(testcases[i].key1_name, testcases[i].key1_id, 478 testcases[i].key2_name, testcases[i].key2_id, 479 testcases[i].alg, testcases[i].type, 480 testcases[i].expect); 481 } 482} 483 484ISC_TEST_LIST_START 485ISC_TEST_ENTRY_CUSTOM(sig_test, setup_test, teardown_test) 486ISC_TEST_ENTRY_CUSTOM(cmp_test, setup_test, teardown_test) 487ISC_TEST_LIST_END 488 489ISC_TEST_MAIN 490