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