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