1/* $NetBSD: openssleddsa_link.c,v 1.1 2024/02/18 20:57:32 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#if !USE_PKCS11 17 18#if HAVE_OPENSSL_ED25519 || HAVE_OPENSSL_ED448 19 20#include <stdbool.h> 21 22#include <openssl/err.h> 23#include <openssl/evp.h> 24#include <openssl/objects.h> 25#include <openssl/x509.h> 26#if !defined(OPENSSL_NO_ENGINE) 27#include <openssl/engine.h> 28#endif /* if !defined(OPENSSL_NO_ENGINE) */ 29 30#include <isc/mem.h> 31#include <isc/result.h> 32#include <isc/safe.h> 33#include <isc/string.h> 34#include <isc/util.h> 35 36#include <dns/keyvalues.h> 37 38#include <dst/result.h> 39 40#include "dst_internal.h" 41#include "dst_openssl.h" 42#include "dst_parse.h" 43#include "openssl_shim.h" 44 45#define DST_RET(a) \ 46 { \ 47 ret = a; \ 48 goto err; \ 49 } 50 51#if HAVE_OPENSSL_ED25519 52#ifndef NID_ED25519 53#error "Ed25519 group is not known (NID_ED25519)" 54#endif /* ifndef NID_ED25519 */ 55#endif /* HAVE_OPENSSL_ED25519 */ 56 57#if HAVE_OPENSSL_ED448 58#ifndef NID_ED448 59#error "Ed448 group is not known (NID_ED448)" 60#endif /* ifndef NID_ED448 */ 61#endif /* HAVE_OPENSSL_ED448 */ 62 63static isc_result_t 64raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key, 65 size_t *key_len, EVP_PKEY **pkey) { 66 isc_result_t ret; 67 int pkey_type = EVP_PKEY_NONE; 68 size_t len = 0; 69 70#if HAVE_OPENSSL_ED25519 71 if (key_alg == DST_ALG_ED25519) { 72 pkey_type = EVP_PKEY_ED25519; 73 len = DNS_KEY_ED25519SIZE; 74 } 75#endif /* HAVE_OPENSSL_ED25519 */ 76#if HAVE_OPENSSL_ED448 77 if (key_alg == DST_ALG_ED448) { 78 pkey_type = EVP_PKEY_ED448; 79 len = DNS_KEY_ED448SIZE; 80 } 81#endif /* HAVE_OPENSSL_ED448 */ 82 if (pkey_type == EVP_PKEY_NONE) { 83 return (ISC_R_NOTIMPLEMENTED); 84 } 85 86 ret = (private ? DST_R_INVALIDPRIVATEKEY : DST_R_INVALIDPUBLICKEY); 87 if (*key_len < len) { 88 return (ret); 89 } 90 91 if (private) { 92 *pkey = EVP_PKEY_new_raw_private_key(pkey_type, NULL, key, len); 93 } else { 94 *pkey = EVP_PKEY_new_raw_public_key(pkey_type, NULL, key, len); 95 } 96 if (*pkey == NULL) { 97 return (dst__openssl_toresult(ret)); 98 } 99 100 *key_len = len; 101 return (ISC_R_SUCCESS); 102} 103 104static isc_result_t 105openssleddsa_fromlabel(dst_key_t *key, const char *engine, const char *label, 106 const char *pin); 107 108static isc_result_t 109openssleddsa_createctx(dst_key_t *key, dst_context_t *dctx) { 110 isc_buffer_t *buf = NULL; 111 112 UNUSED(key); 113 REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 || 114 dctx->key->key_alg == DST_ALG_ED448); 115 116 isc_buffer_allocate(dctx->mctx, &buf, 64); 117 dctx->ctxdata.generic = buf; 118 119 return (ISC_R_SUCCESS); 120} 121 122static void 123openssleddsa_destroyctx(dst_context_t *dctx) { 124 isc_buffer_t *buf = (isc_buffer_t *)dctx->ctxdata.generic; 125 126 REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 || 127 dctx->key->key_alg == DST_ALG_ED448); 128 if (buf != NULL) { 129 isc_buffer_free(&buf); 130 } 131 dctx->ctxdata.generic = NULL; 132} 133 134static isc_result_t 135openssleddsa_adddata(dst_context_t *dctx, const isc_region_t *data) { 136 isc_buffer_t *buf = (isc_buffer_t *)dctx->ctxdata.generic; 137 isc_buffer_t *nbuf = NULL; 138 isc_region_t r; 139 unsigned int length; 140 isc_result_t result; 141 142 REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 || 143 dctx->key->key_alg == DST_ALG_ED448); 144 145 result = isc_buffer_copyregion(buf, data); 146 if (result == ISC_R_SUCCESS) { 147 return (ISC_R_SUCCESS); 148 } 149 150 length = isc_buffer_length(buf) + data->length + 64; 151 isc_buffer_allocate(dctx->mctx, &nbuf, length); 152 isc_buffer_usedregion(buf, &r); 153 (void)isc_buffer_copyregion(nbuf, &r); 154 (void)isc_buffer_copyregion(nbuf, data); 155 isc_buffer_free(&buf); 156 dctx->ctxdata.generic = nbuf; 157 158 return (ISC_R_SUCCESS); 159} 160 161static isc_result_t 162openssleddsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { 163 isc_result_t ret; 164 dst_key_t *key = dctx->key; 165 isc_region_t tbsreg; 166 isc_region_t sigreg; 167 EVP_PKEY *pkey = key->keydata.pkey; 168 EVP_MD_CTX *ctx = EVP_MD_CTX_new(); 169 isc_buffer_t *buf = (isc_buffer_t *)dctx->ctxdata.generic; 170 size_t siglen; 171 172 REQUIRE(key->key_alg == DST_ALG_ED25519 || 173 key->key_alg == DST_ALG_ED448); 174 175 if (ctx == NULL) { 176 return (ISC_R_NOMEMORY); 177 } 178 179 if (key->key_alg == DST_ALG_ED25519) { 180 siglen = DNS_SIG_ED25519SIZE; 181 } else { 182 siglen = DNS_SIG_ED448SIZE; 183 } 184 185 isc_buffer_availableregion(sig, &sigreg); 186 if (sigreg.length < (unsigned int)siglen) { 187 DST_RET(ISC_R_NOSPACE); 188 } 189 190 isc_buffer_usedregion(buf, &tbsreg); 191 192 if (EVP_DigestSignInit(ctx, NULL, NULL, NULL, pkey) != 1) { 193 DST_RET(dst__openssl_toresult3( 194 dctx->category, "EVP_DigestSignInit", ISC_R_FAILURE)); 195 } 196 if (EVP_DigestSign(ctx, sigreg.base, &siglen, tbsreg.base, 197 tbsreg.length) != 1) 198 { 199 DST_RET(dst__openssl_toresult3(dctx->category, "EVP_DigestSign", 200 DST_R_SIGNFAILURE)); 201 } 202 isc_buffer_add(sig, (unsigned int)siglen); 203 ret = ISC_R_SUCCESS; 204 205err: 206 EVP_MD_CTX_free(ctx); 207 isc_buffer_free(&buf); 208 dctx->ctxdata.generic = NULL; 209 210 return (ret); 211} 212 213static isc_result_t 214openssleddsa_verify(dst_context_t *dctx, const isc_region_t *sig) { 215 isc_result_t ret; 216 dst_key_t *key = dctx->key; 217 int status; 218 isc_region_t tbsreg; 219 EVP_PKEY *pkey = key->keydata.pkey; 220 EVP_MD_CTX *ctx = EVP_MD_CTX_new(); 221 isc_buffer_t *buf = (isc_buffer_t *)dctx->ctxdata.generic; 222 unsigned int siglen = 0; 223 224 REQUIRE(key->key_alg == DST_ALG_ED25519 || 225 key->key_alg == DST_ALG_ED448); 226 227 if (ctx == NULL) { 228 return (ISC_R_NOMEMORY); 229 } 230 231#if HAVE_OPENSSL_ED25519 232 if (key->key_alg == DST_ALG_ED25519) { 233 siglen = DNS_SIG_ED25519SIZE; 234 } 235#endif /* if HAVE_OPENSSL_ED25519 */ 236#if HAVE_OPENSSL_ED448 237 if (key->key_alg == DST_ALG_ED448) { 238 siglen = DNS_SIG_ED448SIZE; 239 } 240#endif /* if HAVE_OPENSSL_ED448 */ 241 if (siglen == 0) { 242 DST_RET(ISC_R_NOTIMPLEMENTED); 243 } 244 245 if (sig->length != siglen) { 246 DST_RET(DST_R_VERIFYFAILURE); 247 } 248 249 isc_buffer_usedregion(buf, &tbsreg); 250 251 if (EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey) != 1) { 252 DST_RET(dst__openssl_toresult3( 253 dctx->category, "EVP_DigestVerifyInit", ISC_R_FAILURE)); 254 } 255 256 status = EVP_DigestVerify(ctx, sig->base, siglen, tbsreg.base, 257 tbsreg.length); 258 259 switch (status) { 260 case 1: 261 ret = ISC_R_SUCCESS; 262 break; 263 case 0: 264 ret = dst__openssl_toresult(DST_R_VERIFYFAILURE); 265 break; 266 default: 267 ret = dst__openssl_toresult3(dctx->category, "EVP_DigestVerify", 268 DST_R_VERIFYFAILURE); 269 break; 270 } 271 272err: 273 EVP_MD_CTX_free(ctx); 274 isc_buffer_free(&buf); 275 dctx->ctxdata.generic = NULL; 276 277 return (ret); 278} 279 280static bool 281openssleddsa_compare(const dst_key_t *key1, const dst_key_t *key2) { 282 int status; 283 EVP_PKEY *pkey1 = key1->keydata.pkey; 284 EVP_PKEY *pkey2 = key2->keydata.pkey; 285 286 if (pkey1 == NULL && pkey2 == NULL) { 287 return (true); 288 } else if (pkey1 == NULL || pkey2 == NULL) { 289 return (false); 290 } 291 292 status = EVP_PKEY_cmp(pkey1, pkey2); 293 if (status == 1) { 294 return (true); 295 } 296 return (false); 297} 298 299static isc_result_t 300openssleddsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { 301 isc_result_t ret; 302 EVP_PKEY *pkey = NULL; 303 EVP_PKEY_CTX *ctx = NULL; 304 int nid = 0, status; 305 306 REQUIRE(key->key_alg == DST_ALG_ED25519 || 307 key->key_alg == DST_ALG_ED448); 308 UNUSED(unused); 309 UNUSED(callback); 310 311#if HAVE_OPENSSL_ED25519 312 if (key->key_alg == DST_ALG_ED25519) { 313 nid = NID_ED25519; 314 key->key_size = DNS_KEY_ED25519SIZE * 8; 315 } 316#endif /* if HAVE_OPENSSL_ED25519 */ 317#if HAVE_OPENSSL_ED448 318 if (key->key_alg == DST_ALG_ED448) { 319 nid = NID_ED448; 320 key->key_size = DNS_KEY_ED448SIZE * 8; 321 } 322#endif /* if HAVE_OPENSSL_ED448 */ 323 if (nid == 0) { 324 return (ISC_R_NOTIMPLEMENTED); 325 } 326 327 ctx = EVP_PKEY_CTX_new_id(nid, NULL); 328 if (ctx == NULL) { 329 return (dst__openssl_toresult2("EVP_PKEY_CTX_new_id", 330 DST_R_OPENSSLFAILURE)); 331 } 332 333 status = EVP_PKEY_keygen_init(ctx); 334 if (status != 1) { 335 DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init", 336 DST_R_OPENSSLFAILURE)); 337 } 338 339 status = EVP_PKEY_keygen(ctx, &pkey); 340 if (status != 1) { 341 DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen", 342 DST_R_OPENSSLFAILURE)); 343 } 344 345 key->keydata.pkey = pkey; 346 ret = ISC_R_SUCCESS; 347 348err: 349 EVP_PKEY_CTX_free(ctx); 350 return (ret); 351} 352 353static bool 354openssleddsa_isprivate(const dst_key_t *key) { 355 EVP_PKEY *pkey = key->keydata.pkey; 356 size_t len; 357 358 if (pkey == NULL) { 359 return (false); 360 } 361 362 if (EVP_PKEY_get_raw_private_key(pkey, NULL, &len) == 1 && len > 0) { 363 return (true); 364 } 365 /* can check if first error is EC_R_INVALID_PRIVATE_KEY */ 366 while (ERR_get_error() != 0) { 367 /**/ 368 } 369 370 return (false); 371} 372 373static void 374openssleddsa_destroy(dst_key_t *key) { 375 EVP_PKEY *pkey = key->keydata.pkey; 376 377 EVP_PKEY_free(pkey); 378 key->keydata.pkey = NULL; 379} 380 381static isc_result_t 382openssleddsa_todns(const dst_key_t *key, isc_buffer_t *data) { 383 EVP_PKEY *pkey = key->keydata.pkey; 384 isc_region_t r; 385 size_t len; 386 387 REQUIRE(pkey != NULL); 388 REQUIRE(key->key_alg == DST_ALG_ED25519 || 389 key->key_alg == DST_ALG_ED448); 390 391 if (key->key_alg == DST_ALG_ED25519) { 392 len = DNS_KEY_ED25519SIZE; 393 } else { 394 len = DNS_KEY_ED448SIZE; 395 } 396 397 isc_buffer_availableregion(data, &r); 398 if (r.length < len) { 399 return (ISC_R_NOSPACE); 400 } 401 402 if (EVP_PKEY_get_raw_public_key(pkey, r.base, &len) != 1) { 403 return (dst__openssl_toresult(ISC_R_FAILURE)); 404 } 405 406 isc_buffer_add(data, len); 407 return (ISC_R_SUCCESS); 408} 409 410static isc_result_t 411openssleddsa_fromdns(dst_key_t *key, isc_buffer_t *data) { 412 isc_result_t ret; 413 isc_region_t r; 414 size_t len; 415 EVP_PKEY *pkey; 416 417 REQUIRE(key->key_alg == DST_ALG_ED25519 || 418 key->key_alg == DST_ALG_ED448); 419 420 isc_buffer_remainingregion(data, &r); 421 if (r.length == 0) { 422 return (ISC_R_SUCCESS); 423 } 424 425 len = r.length; 426 ret = raw_key_to_ossl(key->key_alg, 0, r.base, &len, &pkey); 427 if (ret != ISC_R_SUCCESS) { 428 return ret; 429 } 430 431 isc_buffer_forward(data, len); 432 key->keydata.pkey = pkey; 433 key->key_size = len * 8; 434 return (ISC_R_SUCCESS); 435} 436 437static isc_result_t 438openssleddsa_tofile(const dst_key_t *key, const char *directory) { 439 isc_result_t ret; 440 dst_private_t priv; 441 unsigned char *buf = NULL; 442 size_t len; 443 int i; 444 445 REQUIRE(key->key_alg == DST_ALG_ED25519 || 446 key->key_alg == DST_ALG_ED448); 447 448 if (key->keydata.pkey == NULL) { 449 return (DST_R_NULLKEY); 450 } 451 452 if (key->external) { 453 priv.nelements = 0; 454 return (dst__privstruct_writefile(key, &priv, directory)); 455 } 456 457 i = 0; 458 459 if (openssleddsa_isprivate(key)) { 460 if (key->key_alg == DST_ALG_ED25519) { 461 len = DNS_KEY_ED25519SIZE; 462 } else { 463 len = DNS_KEY_ED448SIZE; 464 } 465 buf = isc_mem_get(key->mctx, len); 466 if (EVP_PKEY_get_raw_private_key(key->keydata.pkey, buf, 467 &len) != 1) 468 { 469 DST_RET(dst__openssl_toresult(ISC_R_FAILURE)); 470 } 471 priv.elements[i].tag = TAG_EDDSA_PRIVATEKEY; 472 priv.elements[i].length = len; 473 priv.elements[i].data = buf; 474 i++; 475 } 476 if (key->engine != NULL) { 477 priv.elements[i].tag = TAG_EDDSA_ENGINE; 478 priv.elements[i].length = (unsigned short)strlen(key->engine) + 479 1; 480 priv.elements[i].data = (unsigned char *)key->engine; 481 i++; 482 } 483 if (key->label != NULL) { 484 priv.elements[i].tag = TAG_EDDSA_LABEL; 485 priv.elements[i].length = (unsigned short)strlen(key->label) + 486 1; 487 priv.elements[i].data = (unsigned char *)key->label; 488 i++; 489 } 490 491 priv.nelements = i; 492 ret = dst__privstruct_writefile(key, &priv, directory); 493 494err: 495 if (buf != NULL) { 496 isc_mem_put(key->mctx, buf, len); 497 } 498 return (ret); 499} 500 501static isc_result_t 502eddsa_check(EVP_PKEY *pkey, EVP_PKEY *pubpkey) { 503 if (pubpkey == NULL) { 504 return (ISC_R_SUCCESS); 505 } 506 if (EVP_PKEY_cmp(pkey, pubpkey) == 1) { 507 return (ISC_R_SUCCESS); 508 } 509 return (ISC_R_FAILURE); 510} 511 512static isc_result_t 513openssleddsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { 514 dst_private_t priv; 515 isc_result_t ret; 516 int i, privkey_index = -1; 517 const char *engine = NULL, *label = NULL; 518 EVP_PKEY *pkey = NULL, *pubpkey = NULL; 519 size_t len; 520 isc_mem_t *mctx = key->mctx; 521 522 REQUIRE(key->key_alg == DST_ALG_ED25519 || 523 key->key_alg == DST_ALG_ED448); 524 525 /* read private key file */ 526 ret = dst__privstruct_parse(key, DST_ALG_ED25519, lexer, mctx, &priv); 527 if (ret != ISC_R_SUCCESS) { 528 goto err; 529 } 530 531 if (key->external) { 532 if (priv.nelements != 0) { 533 DST_RET(DST_R_INVALIDPRIVATEKEY); 534 } 535 if (pub == NULL) { 536 DST_RET(DST_R_INVALIDPRIVATEKEY); 537 } 538 key->keydata.pkey = pub->keydata.pkey; 539 pub->keydata.pkey = NULL; 540 dst__privstruct_free(&priv, mctx); 541 isc_safe_memwipe(&priv, sizeof(priv)); 542 return (ISC_R_SUCCESS); 543 } 544 545 if (pub != NULL) { 546 pubpkey = pub->keydata.pkey; 547 } 548 549 for (i = 0; i < priv.nelements; i++) { 550 switch (priv.elements[i].tag) { 551 case TAG_EDDSA_ENGINE: 552 engine = (char *)priv.elements[i].data; 553 break; 554 case TAG_EDDSA_LABEL: 555 label = (char *)priv.elements[i].data; 556 break; 557 case TAG_EDDSA_PRIVATEKEY: 558 privkey_index = i; 559 break; 560 default: 561 break; 562 } 563 } 564 565 if (label != NULL) { 566 ret = openssleddsa_fromlabel(key, engine, label, NULL); 567 if (ret != ISC_R_SUCCESS) { 568 goto err; 569 } 570 if (eddsa_check(key->keydata.pkey, pubpkey) != ISC_R_SUCCESS) { 571 DST_RET(DST_R_INVALIDPRIVATEKEY); 572 } 573 DST_RET(ISC_R_SUCCESS); 574 } 575 576 if (privkey_index < 0) { 577 DST_RET(DST_R_INVALIDPRIVATEKEY); 578 } 579 580 len = priv.elements[privkey_index].length; 581 ret = raw_key_to_ossl(key->key_alg, 1, 582 priv.elements[privkey_index].data, &len, &pkey); 583 if (ret != ISC_R_SUCCESS) { 584 goto err; 585 } 586 if (eddsa_check(pkey, pubpkey) != ISC_R_SUCCESS) { 587 EVP_PKEY_free(pkey); 588 DST_RET(DST_R_INVALIDPRIVATEKEY); 589 } 590 key->keydata.pkey = pkey; 591 key->key_size = len * 8; 592 ret = ISC_R_SUCCESS; 593 594err: 595 dst__privstruct_free(&priv, mctx); 596 isc_safe_memwipe(&priv, sizeof(priv)); 597 return (ret); 598} 599 600static isc_result_t 601openssleddsa_fromlabel(dst_key_t *key, const char *engine, const char *label, 602 const char *pin) { 603#if !defined(OPENSSL_NO_ENGINE) 604 isc_result_t ret; 605 ENGINE *e; 606 EVP_PKEY *pkey = NULL, *pubpkey = NULL; 607 int baseid = EVP_PKEY_NONE; 608 609 UNUSED(pin); 610 611 REQUIRE(key->key_alg == DST_ALG_ED25519 || 612 key->key_alg == DST_ALG_ED448); 613 614#if HAVE_OPENSSL_ED25519 615 if (key->key_alg == DST_ALG_ED25519) { 616 baseid = EVP_PKEY_ED25519; 617 } 618#endif /* if HAVE_OPENSSL_ED25519 */ 619#if HAVE_OPENSSL_ED448 620 if (key->key_alg == DST_ALG_ED448) { 621 baseid = EVP_PKEY_ED448; 622 } 623#endif /* if HAVE_OPENSSL_ED448 */ 624 if (baseid == EVP_PKEY_NONE) { 625 return (ISC_R_NOTIMPLEMENTED); 626 } 627 628 if (engine == NULL) { 629 return (DST_R_NOENGINE); 630 } 631 e = dst__openssl_getengine(engine); 632 if (e == NULL) { 633 return (DST_R_NOENGINE); 634 } 635 pkey = ENGINE_load_private_key(e, label, NULL, NULL); 636 if (pkey == NULL) { 637 return (dst__openssl_toresult2("ENGINE_load_private_key", 638 ISC_R_NOTFOUND)); 639 } 640 if (EVP_PKEY_base_id(pkey) != baseid) { 641 DST_RET(DST_R_INVALIDPRIVATEKEY); 642 } 643 644 pubpkey = ENGINE_load_public_key(e, label, NULL, NULL); 645 if (eddsa_check(pkey, pubpkey) != ISC_R_SUCCESS) { 646 DST_RET(DST_R_INVALIDPRIVATEKEY); 647 } 648 649 key->engine = isc_mem_strdup(key->mctx, engine); 650 key->label = isc_mem_strdup(key->mctx, label); 651 key->key_size = EVP_PKEY_bits(pkey); 652 key->keydata.pkey = pkey; 653 pkey = NULL; 654 ret = ISC_R_SUCCESS; 655 656err: 657 if (pubpkey != NULL) { 658 EVP_PKEY_free(pubpkey); 659 } 660 if (pkey != NULL) { 661 EVP_PKEY_free(pkey); 662 } 663 return (ret); 664#else /* if !defined(OPENSSL_NO_ENGINE) */ 665 UNUSED(key); 666 UNUSED(engine); 667 UNUSED(label); 668 UNUSED(pin); 669 return (DST_R_NOENGINE); 670#endif /* if !defined(OPENSSL_NO_ENGINE) */ 671} 672 673static dst_func_t openssleddsa_functions = { 674 openssleddsa_createctx, 675 NULL, /*%< createctx2 */ 676 openssleddsa_destroyctx, 677 openssleddsa_adddata, 678 openssleddsa_sign, 679 openssleddsa_verify, 680 NULL, /*%< verify2 */ 681 NULL, /*%< computesecret */ 682 openssleddsa_compare, 683 NULL, /*%< paramcompare */ 684 openssleddsa_generate, 685 openssleddsa_isprivate, 686 openssleddsa_destroy, 687 openssleddsa_todns, 688 openssleddsa_fromdns, 689 openssleddsa_tofile, 690 openssleddsa_parse, 691 NULL, /*%< cleanup */ 692 openssleddsa_fromlabel, 693 NULL, /*%< dump */ 694 NULL, /*%< restore */ 695}; 696 697isc_result_t 698dst__openssleddsa_init(dst_func_t **funcp) { 699 REQUIRE(funcp != NULL); 700 if (*funcp == NULL) { 701 *funcp = &openssleddsa_functions; 702 } 703 return (ISC_R_SUCCESS); 704} 705 706#endif /* HAVE_OPENSSL_ED25519 || HAVE_OPENSSL_ED448 */ 707 708#endif /* !USE_PKCS11 */ 709 710/*! \file */ 711