xref: /netbsd-src/external/mpl/bind/dist/lib/dns/opensslecdsa_link.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: opensslecdsa_link.c,v 1.9 2025/01/26 16:25:23 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 /*! \file */
17 
18 #include <stdbool.h>
19 
20 #include <openssl/bn.h>
21 #include <openssl/ecdsa.h>
22 #include <openssl/err.h>
23 #include <openssl/evp.h>
24 #include <openssl/objects.h>
25 #include <openssl/opensslv.h>
26 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
27 #include <openssl/core_names.h>
28 #include <openssl/param_build.h>
29 #endif
30 
31 #include <isc/mem.h>
32 #include <isc/result.h>
33 #include <isc/safe.h>
34 #include <isc/string.h>
35 #include <isc/util.h>
36 
37 #include <dns/keyvalues.h>
38 
39 #include "dst_internal.h"
40 #include "dst_openssl.h"
41 #include "dst_parse.h"
42 #include "openssl_shim.h"
43 
44 #ifndef NID_X9_62_prime256v1
45 #error "P-256 group is not known (NID_X9_62_prime256v1)"
46 #endif /* ifndef NID_X9_62_prime256v1 */
47 #ifndef NID_secp384r1
48 #error "P-384 group is not known (NID_secp384r1)"
49 #endif /* ifndef NID_secp384r1 */
50 
51 #define MAX_PUBKEY_SIZE DNS_KEY_ECDSA384SIZE
52 
53 #define MAX_PRIVKEY_SIZE (MAX_PUBKEY_SIZE / 2)
54 
55 #define DST_RET(a)        \
56 	{                 \
57 		ret = a;  \
58 		goto err; \
59 	}
60 
61 static bool
62 opensslecdsa_valid_key_alg(unsigned int key_alg) {
63 	switch (key_alg) {
64 	case DST_ALG_ECDSA256:
65 	case DST_ALG_ECDSA384:
66 		return true;
67 	default:
68 		return false;
69 	}
70 }
71 
72 static int
73 opensslecdsa_key_alg_to_group_nid(unsigned int key_alg) {
74 	switch (key_alg) {
75 	case DST_ALG_ECDSA256:
76 		return NID_X9_62_prime256v1;
77 	case DST_ALG_ECDSA384:
78 		return NID_secp384r1;
79 	default:
80 		UNREACHABLE();
81 	}
82 }
83 
84 static size_t
85 opensslecdsa_key_alg_to_publickey_size(unsigned int key_alg) {
86 	switch (key_alg) {
87 	case DST_ALG_ECDSA256:
88 		return DNS_KEY_ECDSA256SIZE;
89 	case DST_ALG_ECDSA384:
90 		return DNS_KEY_ECDSA384SIZE;
91 	default:
92 		UNREACHABLE();
93 	}
94 }
95 
96 /*
97  * OpenSSL requires us to set the public key portion, but since our private key
98  * file format does not contain it directly, we generate it as needed.
99  */
100 static EC_POINT *
101 opensslecdsa_generate_public_key(const EC_GROUP *group, const BIGNUM *privkey) {
102 	EC_POINT *pubkey = EC_POINT_new(group);
103 	if (pubkey == NULL) {
104 		return NULL;
105 	}
106 	if (EC_POINT_mul(group, pubkey, privkey, NULL, NULL, NULL) != 1) {
107 		EC_POINT_free(pubkey);
108 		return NULL;
109 	}
110 	return pubkey;
111 }
112 
113 static int
114 BN_bn2bin_fixed(const BIGNUM *bn, unsigned char *buf, int size) {
115 	int bytes = size - BN_num_bytes(bn);
116 
117 	INSIST(bytes >= 0);
118 
119 	while (bytes-- > 0) {
120 		*buf++ = 0;
121 	}
122 	BN_bn2bin(bn, buf);
123 	return size;
124 }
125 
126 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
127 
128 static const char *
129 opensslecdsa_key_alg_to_group_name(unsigned int key_alg) {
130 	switch (key_alg) {
131 	case DST_ALG_ECDSA256:
132 		return "prime256v1";
133 	case DST_ALG_ECDSA384:
134 		return "secp384r1";
135 	default:
136 		UNREACHABLE();
137 	}
138 }
139 
140 static isc_result_t
141 opensslecdsa_create_pkey_params(unsigned int key_alg, bool private,
142 				const unsigned char *key, size_t key_len,
143 				EVP_PKEY **pkey) {
144 	isc_result_t ret;
145 	int status;
146 	int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg);
147 	const char *groupname = opensslecdsa_key_alg_to_group_name(key_alg);
148 	OSSL_PARAM_BLD *bld = NULL;
149 	OSSL_PARAM *params = NULL;
150 	EVP_PKEY_CTX *ctx = NULL;
151 	EC_POINT *pubkey = NULL;
152 	EC_GROUP *group = NULL;
153 	BIGNUM *priv = NULL;
154 	unsigned char buf[MAX_PUBKEY_SIZE + 1];
155 
156 	bld = OSSL_PARAM_BLD_new();
157 	if (bld == NULL) {
158 		DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_new",
159 					       DST_R_OPENSSLFAILURE));
160 	}
161 	status = OSSL_PARAM_BLD_push_utf8_string(
162 		bld, OSSL_PKEY_PARAM_GROUP_NAME, groupname, 0);
163 	if (status != 1) {
164 		DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_"
165 					       "utf8_string",
166 					       DST_R_OPENSSLFAILURE));
167 	}
168 
169 	if (private) {
170 		group = EC_GROUP_new_by_curve_name(group_nid);
171 		if (group == NULL) {
172 			DST_RET(dst__openssl_toresult2("EC_GROUP_new_by_"
173 						       "curve_name",
174 						       DST_R_OPENSSLFAILURE));
175 		}
176 
177 		priv = BN_bin2bn(key, key_len, NULL);
178 		if (priv == NULL) {
179 			DST_RET(dst__openssl_toresult2("BN_bin2bn",
180 						       DST_R_OPENSSLFAILURE));
181 		}
182 
183 		status = OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY,
184 						priv);
185 		if (status != 1) {
186 			DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
187 						       DST_R_OPENSSLFAILURE));
188 		}
189 
190 		pubkey = opensslecdsa_generate_public_key(group, priv);
191 		if (pubkey == NULL) {
192 			DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
193 		}
194 
195 		key = buf;
196 		key_len = EC_POINT_point2oct(group, pubkey,
197 					     POINT_CONVERSION_UNCOMPRESSED, buf,
198 					     sizeof(buf), NULL);
199 		if (key_len == 0) {
200 			DST_RET(dst__openssl_toresult2("EC_POINT_point2oct",
201 						       DST_R_OPENSSLFAILURE));
202 		}
203 	} else {
204 		INSIST(key_len + 1 <= sizeof(buf));
205 		buf[0] = POINT_CONVERSION_UNCOMPRESSED;
206 		memmove(buf + 1, key, key_len);
207 		key = buf;
208 		key_len = key_len + 1;
209 	}
210 
211 	status = OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY,
212 						  key, key_len);
213 	if (status != 1) {
214 		DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_"
215 					       "octet_string",
216 					       DST_R_OPENSSLFAILURE));
217 	}
218 
219 	params = OSSL_PARAM_BLD_to_param(bld);
220 	if (params == NULL) {
221 		DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_to_param",
222 					       DST_R_OPENSSLFAILURE));
223 	}
224 	ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
225 	if (ctx == NULL) {
226 		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name",
227 					       DST_R_OPENSSLFAILURE));
228 	}
229 	status = EVP_PKEY_fromdata_init(ctx);
230 	if (status != 1) {
231 		/* This will fail if the default provider is an engine.
232 		 * Return ISC_R_FAILURE to retry using the legacy API. */
233 		DST_RET(dst__openssl_toresult(ISC_R_FAILURE));
234 	}
235 	status = EVP_PKEY_fromdata(
236 		ctx, pkey, private ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY,
237 		params);
238 	if (status != 1 || *pkey == NULL) {
239 		DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata",
240 					       DST_R_OPENSSLFAILURE));
241 	}
242 
243 	ret = ISC_R_SUCCESS;
244 
245 err:
246 	OSSL_PARAM_free(params);
247 	OSSL_PARAM_BLD_free(bld);
248 	EVP_PKEY_CTX_free(ctx);
249 	BN_clear_free(priv);
250 	EC_POINT_free(pubkey);
251 	EC_GROUP_free(group);
252 
253 	return ret;
254 }
255 
256 static bool
257 opensslecdsa_extract_public_key_params(const dst_key_t *key, unsigned char *dst,
258 				       size_t dstlen) {
259 	EVP_PKEY *pkey = key->keydata.pkeypair.pub;
260 	BIGNUM *x = NULL;
261 	BIGNUM *y = NULL;
262 	bool ret = false;
263 
264 	if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &x) == 1 &&
265 	    EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y) == 1)
266 	{
267 		BN_bn2bin_fixed(x, &dst[0], dstlen / 2);
268 		BN_bn2bin_fixed(y, &dst[dstlen / 2], dstlen / 2);
269 		ret = true;
270 	}
271 	BN_clear_free(x);
272 	BN_clear_free(y);
273 	return ret;
274 }
275 
276 #endif
277 
278 #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
279 
280 static isc_result_t
281 opensslecdsa_create_pkey_legacy(unsigned int key_alg, bool private,
282 				const unsigned char *key, size_t key_len,
283 				EVP_PKEY **retkey) {
284 	isc_result_t ret = ISC_R_SUCCESS;
285 	EC_KEY *eckey = NULL;
286 	EVP_PKEY *pkey = NULL;
287 	BIGNUM *privkey = NULL;
288 	EC_POINT *pubkey = NULL;
289 	unsigned char buf[MAX_PUBKEY_SIZE + 1];
290 	int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg);
291 
292 	eckey = EC_KEY_new_by_curve_name(group_nid);
293 	if (eckey == NULL) {
294 		DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
295 	}
296 
297 	if (private) {
298 		const EC_GROUP *group = EC_KEY_get0_group(eckey);
299 
300 		privkey = BN_bin2bn(key, key_len, NULL);
301 		if (privkey == NULL) {
302 			DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
303 		}
304 		if (!EC_KEY_set_private_key(eckey, privkey)) {
305 			DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY));
306 		}
307 
308 		pubkey = opensslecdsa_generate_public_key(group, privkey);
309 		if (pubkey == NULL) {
310 			DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
311 		}
312 		if (EC_KEY_set_public_key(eckey, pubkey) != 1) {
313 			DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
314 		}
315 	} else {
316 		const unsigned char *cp = buf;
317 		INSIST(key_len + 1 <= sizeof(buf));
318 		buf[0] = POINT_CONVERSION_UNCOMPRESSED;
319 		memmove(buf + 1, key, key_len);
320 		if (o2i_ECPublicKey(&eckey, &cp, key_len + 1) == NULL) {
321 			DST_RET(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY));
322 		}
323 		if (EC_KEY_check_key(eckey) != 1) {
324 			DST_RET(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY));
325 		}
326 	}
327 
328 	pkey = EVP_PKEY_new();
329 	if (pkey == NULL) {
330 		DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
331 	}
332 	if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) {
333 		DST_RET(dst__openssl_toresult(ISC_R_FAILURE));
334 	}
335 
336 	*retkey = pkey;
337 	pkey = NULL;
338 
339 err:
340 	BN_clear_free(privkey);
341 	EC_POINT_free(pubkey);
342 	EC_KEY_free(eckey);
343 	EVP_PKEY_free(pkey);
344 	return ret;
345 }
346 
347 static bool
348 opensslecdsa_extract_public_key_legacy(const dst_key_t *key, unsigned char *dst,
349 				       size_t dstlen) {
350 	EVP_PKEY *pkey = key->keydata.pkeypair.pub;
351 	const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey);
352 	const EC_GROUP *group = (eckey == NULL) ? NULL
353 						: EC_KEY_get0_group(eckey);
354 	const EC_POINT *pub = (eckey == NULL) ? NULL
355 					      : EC_KEY_get0_public_key(eckey);
356 	unsigned char buf[MAX_PUBKEY_SIZE + 1];
357 	size_t len;
358 
359 	if (group == NULL || pub == NULL) {
360 		return false;
361 	}
362 
363 	len = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED, buf,
364 				 sizeof(buf), NULL);
365 	if (len == dstlen + 1) {
366 		memmove(dst, buf + 1, dstlen);
367 		return true;
368 	}
369 	return false;
370 }
371 
372 #endif
373 
374 static bool
375 opensslecdsa_extract_public_key(const dst_key_t *key, unsigned char *dst,
376 				size_t dstlen) {
377 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
378 	if (opensslecdsa_extract_public_key_params(key, dst, dstlen)) {
379 		return true;
380 	}
381 #endif
382 #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
383 	if (opensslecdsa_extract_public_key_legacy(key, dst, dstlen)) {
384 		return true;
385 	}
386 #endif
387 	return false;
388 }
389 
390 static isc_result_t
391 opensslecdsa_create_pkey(unsigned int key_alg, bool private,
392 			 const unsigned char *key, size_t key_len,
393 			 EVP_PKEY **retkey) {
394 	isc_result_t ret;
395 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
396 	ret = opensslecdsa_create_pkey_params(key_alg, private, key, key_len,
397 					      retkey);
398 	if (ret != ISC_R_FAILURE) {
399 		return ret;
400 	}
401 #endif
402 #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
403 	ret = opensslecdsa_create_pkey_legacy(key_alg, private, key, key_len,
404 					      retkey);
405 	if (ret == ISC_R_SUCCESS) {
406 		return ret;
407 	}
408 #endif
409 	return DST_R_OPENSSLFAILURE;
410 }
411 
412 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
413 
414 static isc_result_t
415 opensslecdsa_generate_pkey_with_uri(int group_nid, const char *label,
416 				    EVP_PKEY **retkey) {
417 	int status;
418 	isc_result_t ret;
419 	char *uri = UNCONST(label);
420 	EVP_PKEY_CTX *ctx = NULL;
421 	OSSL_PARAM params[3];
422 
423 	/* Generate the key's parameters. */
424 	params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_uri", uri, 0);
425 	params[1] = OSSL_PARAM_construct_utf8_string(
426 		"pkcs11_key_usage", (char *)"digitalSignature", 0);
427 	params[2] = OSSL_PARAM_construct_end();
428 
429 	ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "provider=pkcs11");
430 	if (ctx == NULL) {
431 		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name",
432 					       DST_R_OPENSSLFAILURE));
433 	}
434 
435 	status = EVP_PKEY_keygen_init(ctx);
436 	if (status != 1) {
437 		DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init",
438 					       DST_R_OPENSSLFAILURE));
439 	}
440 
441 	status = EVP_PKEY_CTX_set_params(ctx, params);
442 	if (status != 1) {
443 		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_set_params",
444 					       DST_R_OPENSSLFAILURE));
445 	}
446 	/*
447 	 * Setting the P-384 curve doesn't work correctly when using:
448 	 * OSSL_PARAM_construct_utf8_string("ec_paramgen_curve", "P-384", 0);
449 	 *
450 	 * Instead use the OpenSSL function to set the curve nid param.
451 	 */
452 	status = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, group_nid);
453 	if (status != 1) {
454 		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_set_ec_paramgen_"
455 					       "curve_nid",
456 					       DST_R_OPENSSLFAILURE));
457 	}
458 
459 	/* Generate the key. */
460 	status = EVP_PKEY_generate(ctx, retkey);
461 	if (status != 1) {
462 		DST_RET(dst__openssl_toresult2("EVP_PKEY_generate",
463 					       DST_R_OPENSSLFAILURE));
464 	}
465 
466 	ret = ISC_R_SUCCESS;
467 
468 err:
469 	EVP_PKEY_CTX_free(ctx);
470 	return ret;
471 }
472 
473 static isc_result_t
474 opensslecdsa_generate_pkey(unsigned int key_alg, const char *label,
475 			   EVP_PKEY **retkey) {
476 	isc_result_t ret;
477 	EVP_PKEY_CTX *ctx = NULL;
478 	EVP_PKEY *params_pkey = NULL;
479 	int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg);
480 	int status;
481 
482 	if (label != NULL) {
483 		return opensslecdsa_generate_pkey_with_uri(group_nid, label,
484 							   retkey);
485 	}
486 
487 	/* Generate the key's parameters. */
488 	ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
489 	if (ctx == NULL) {
490 		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name",
491 					       DST_R_OPENSSLFAILURE));
492 	}
493 	status = EVP_PKEY_paramgen_init(ctx);
494 	if (status != 1) {
495 		DST_RET(dst__openssl_toresult2("EVP_PKEY_paramgen_init",
496 					       DST_R_OPENSSLFAILURE));
497 	}
498 	status = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, group_nid);
499 	if (status != 1) {
500 		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_set_ec_paramgen_"
501 					       "curve_nid",
502 					       DST_R_OPENSSLFAILURE));
503 	}
504 	status = EVP_PKEY_paramgen(ctx, &params_pkey);
505 	if (status != 1 || params_pkey == NULL) {
506 		DST_RET(dst__openssl_toresult2("EVP_PKEY_paramgen",
507 					       DST_R_OPENSSLFAILURE));
508 	}
509 	EVP_PKEY_CTX_free(ctx);
510 
511 	/* Generate the key. */
512 	ctx = EVP_PKEY_CTX_new(params_pkey, NULL);
513 	if (ctx == NULL) {
514 		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new",
515 					       DST_R_OPENSSLFAILURE));
516 	}
517 	status = EVP_PKEY_keygen_init(ctx);
518 	if (status != 1) {
519 		DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init",
520 					       DST_R_OPENSSLFAILURE));
521 	}
522 
523 	status = EVP_PKEY_keygen(ctx, retkey);
524 	if (status != 1) {
525 		DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen",
526 					       DST_R_OPENSSLFAILURE));
527 	}
528 	ret = ISC_R_SUCCESS;
529 
530 err:
531 	EVP_PKEY_free(params_pkey);
532 	EVP_PKEY_CTX_free(ctx);
533 	return ret;
534 }
535 
536 static isc_result_t
537 opensslecdsa_validate_pkey_group(unsigned int key_alg, EVP_PKEY *pkey) {
538 	const char *groupname = opensslecdsa_key_alg_to_group_name(key_alg);
539 	char gname[64];
540 
541 	if (EVP_PKEY_get_group_name(pkey, gname, sizeof(gname), NULL) != 1) {
542 		return DST_R_INVALIDPRIVATEKEY;
543 	}
544 	if (strcmp(gname, groupname) != 0) {
545 		return DST_R_INVALIDPRIVATEKEY;
546 	}
547 	return ISC_R_SUCCESS;
548 }
549 
550 static bool
551 opensslecdsa_extract_private_key(const dst_key_t *key, unsigned char *buf,
552 				 size_t buflen) {
553 	EVP_PKEY *pkey = key->keydata.pkeypair.priv;
554 	BIGNUM *priv = NULL;
555 
556 	if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &priv) != 1) {
557 		return false;
558 	}
559 
560 	BN_bn2bin_fixed(priv, buf, buflen);
561 	BN_clear_free(priv);
562 	return true;
563 }
564 
565 #else
566 
567 static isc_result_t
568 opensslecdsa_generate_pkey(unsigned int key_alg, const char *label,
569 			   EVP_PKEY **retkey) {
570 	isc_result_t ret;
571 	EC_KEY *eckey = NULL;
572 	EVP_PKEY *pkey = NULL;
573 	int group_nid;
574 
575 	UNUSED(label);
576 
577 	group_nid = opensslecdsa_key_alg_to_group_nid(key_alg);
578 
579 	eckey = EC_KEY_new_by_curve_name(group_nid);
580 	if (eckey == NULL) {
581 		DST_RET(dst__openssl_toresult2("EC_KEY_new_by_curve_name",
582 					       DST_R_OPENSSLFAILURE));
583 	}
584 
585 	if (EC_KEY_generate_key(eckey) != 1) {
586 		DST_RET(dst__openssl_toresult2("EC_KEY_generate_key",
587 					       DST_R_OPENSSLFAILURE));
588 	}
589 
590 	pkey = EVP_PKEY_new();
591 	if (pkey == NULL) {
592 		DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
593 	}
594 	if (EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
595 		DST_RET(dst__openssl_toresult2("EVP_PKEY_set1_EC_KEY",
596 					       DST_R_OPENSSLFAILURE));
597 	}
598 	*retkey = pkey;
599 	pkey = NULL;
600 	ret = ISC_R_SUCCESS;
601 
602 err:
603 	EC_KEY_free(eckey);
604 	EVP_PKEY_free(pkey);
605 	return ret;
606 }
607 
608 static isc_result_t
609 opensslecdsa_validate_pkey_group(unsigned int key_alg, EVP_PKEY *pkey) {
610 	const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey);
611 	int group_nid;
612 
613 	if (eckey == NULL) {
614 		return dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY);
615 	}
616 
617 	group_nid = opensslecdsa_key_alg_to_group_nid(key_alg);
618 
619 	if (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey)) != group_nid) {
620 		return DST_R_INVALIDPRIVATEKEY;
621 	}
622 
623 	return ISC_R_SUCCESS;
624 }
625 
626 static bool
627 opensslecdsa_extract_private_key(const dst_key_t *key, unsigned char *buf,
628 				 size_t buflen) {
629 	const EC_KEY *eckey = NULL;
630 	const BIGNUM *privkey = NULL;
631 
632 	eckey = EVP_PKEY_get0_EC_KEY(key->keydata.pkeypair.priv);
633 	if (eckey == NULL) {
634 		ERR_clear_error();
635 		return false;
636 	}
637 
638 	privkey = EC_KEY_get0_private_key(eckey);
639 	if (privkey == NULL) {
640 		ERR_clear_error();
641 		return false;
642 	}
643 
644 	BN_bn2bin_fixed(privkey, buf, buflen);
645 	return true;
646 }
647 
648 #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
649 
650 static isc_result_t
651 opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) {
652 	isc_result_t ret = ISC_R_SUCCESS;
653 	EVP_MD_CTX *evp_md_ctx;
654 	const EVP_MD *type = NULL;
655 
656 	UNUSED(key);
657 	REQUIRE(opensslecdsa_valid_key_alg(dctx->key->key_alg));
658 	REQUIRE(dctx->use == DO_SIGN || dctx->use == DO_VERIFY);
659 
660 	evp_md_ctx = EVP_MD_CTX_create();
661 	if (evp_md_ctx == NULL) {
662 		DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
663 	}
664 	if (dctx->key->key_alg == DST_ALG_ECDSA256) {
665 		type = EVP_sha256();
666 	} else {
667 		type = EVP_sha384();
668 	}
669 
670 	if (dctx->use == DO_SIGN) {
671 		if (EVP_DigestSignInit(evp_md_ctx, NULL, type, NULL,
672 				       dctx->key->keydata.pkeypair.priv) != 1)
673 		{
674 			EVP_MD_CTX_destroy(evp_md_ctx);
675 			DST_RET(dst__openssl_toresult3(dctx->category,
676 						       "EVP_DigestSignInit",
677 						       ISC_R_FAILURE));
678 		}
679 	} else {
680 		if (EVP_DigestVerifyInit(evp_md_ctx, NULL, type, NULL,
681 					 dctx->key->keydata.pkeypair.pub) != 1)
682 		{
683 			EVP_MD_CTX_destroy(evp_md_ctx);
684 			DST_RET(dst__openssl_toresult3(dctx->category,
685 						       "EVP_DigestVerifyInit",
686 						       ISC_R_FAILURE));
687 		}
688 	}
689 
690 	dctx->ctxdata.evp_md_ctx = evp_md_ctx;
691 
692 err:
693 	return ret;
694 }
695 
696 static void
697 opensslecdsa_destroyctx(dst_context_t *dctx) {
698 	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
699 
700 	REQUIRE(opensslecdsa_valid_key_alg(dctx->key->key_alg));
701 	REQUIRE(dctx->use == DO_SIGN || dctx->use == DO_VERIFY);
702 
703 	if (evp_md_ctx != NULL) {
704 		EVP_MD_CTX_destroy(evp_md_ctx);
705 		dctx->ctxdata.evp_md_ctx = NULL;
706 	}
707 }
708 
709 static isc_result_t
710 opensslecdsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
711 	isc_result_t ret = ISC_R_SUCCESS;
712 	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
713 
714 	REQUIRE(opensslecdsa_valid_key_alg(dctx->key->key_alg));
715 	REQUIRE(dctx->use == DO_SIGN || dctx->use == DO_VERIFY);
716 
717 	if (dctx->use == DO_SIGN) {
718 		if (EVP_DigestSignUpdate(evp_md_ctx, data->base,
719 					 data->length) != 1)
720 		{
721 			DST_RET(dst__openssl_toresult3(dctx->category,
722 						       "EVP_DigestSignUpdate",
723 						       ISC_R_FAILURE));
724 		}
725 	} else {
726 		if (EVP_DigestVerifyUpdate(evp_md_ctx, data->base,
727 					   data->length) != 1)
728 		{
729 			DST_RET(dst__openssl_toresult3(dctx->category,
730 						       "EVP_DigestVerifyUpdate",
731 						       ISC_R_FAILURE));
732 		}
733 	}
734 
735 err:
736 	return ret;
737 }
738 
739 static isc_result_t
740 opensslecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
741 	isc_result_t ret;
742 	dst_key_t *key = dctx->key;
743 	isc_region_t region;
744 	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
745 	ECDSA_SIG *ecdsasig = NULL;
746 	size_t siglen, sigder_len = 0, sigder_alloced = 0;
747 	unsigned char *sigder = NULL;
748 	const unsigned char *sigder_copy;
749 	const BIGNUM *r, *s;
750 
751 	REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
752 	REQUIRE(dctx->use == DO_SIGN);
753 
754 	if (key->key_alg == DST_ALG_ECDSA256) {
755 		siglen = DNS_SIG_ECDSA256SIZE;
756 	} else {
757 		siglen = DNS_SIG_ECDSA384SIZE;
758 	}
759 
760 	isc_buffer_availableregion(sig, &region);
761 	if (region.length < siglen) {
762 		DST_RET(ISC_R_NOSPACE);
763 	}
764 
765 	if (EVP_DigestSignFinal(evp_md_ctx, NULL, &sigder_len) != 1) {
766 		DST_RET(dst__openssl_toresult3(
767 			dctx->category, "EVP_DigestSignFinal", ISC_R_FAILURE));
768 	}
769 	if (sigder_len == 0) {
770 		DST_RET(ISC_R_FAILURE);
771 	}
772 	sigder = isc_mem_get(dctx->mctx, sigder_len);
773 	sigder_alloced = sigder_len;
774 	if (EVP_DigestSignFinal(evp_md_ctx, sigder, &sigder_len) != 1) {
775 		DST_RET(dst__openssl_toresult3(
776 			dctx->category, "EVP_DigestSignFinal", ISC_R_FAILURE));
777 	}
778 	sigder_copy = sigder;
779 	if (d2i_ECDSA_SIG(&ecdsasig, &sigder_copy, sigder_len) == NULL) {
780 		DST_RET(dst__openssl_toresult3(dctx->category, "d2i_ECDSA_SIG",
781 					       ISC_R_FAILURE));
782 	}
783 
784 	ECDSA_SIG_get0(ecdsasig, &r, &s);
785 	BN_bn2bin_fixed(r, region.base, siglen / 2);
786 	isc_region_consume(&region, siglen / 2);
787 	BN_bn2bin_fixed(s, region.base, siglen / 2);
788 	isc_region_consume(&region, siglen / 2);
789 	ECDSA_SIG_free(ecdsasig);
790 	isc_buffer_add(sig, siglen);
791 	ret = ISC_R_SUCCESS;
792 
793 err:
794 	if (sigder != NULL && sigder_alloced != 0) {
795 		isc_mem_put(dctx->mctx, sigder, sigder_alloced);
796 	}
797 
798 	return ret;
799 }
800 
801 static isc_result_t
802 opensslecdsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
803 	isc_result_t ret;
804 	dst_key_t *key = dctx->key;
805 	int status;
806 	unsigned char *cp = sig->base;
807 	ECDSA_SIG *ecdsasig = NULL;
808 	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
809 	size_t siglen, sigder_len = 0, sigder_alloced = 0;
810 	unsigned char *sigder = NULL;
811 	unsigned char *sigder_copy;
812 	BIGNUM *r = NULL, *s = NULL;
813 
814 	REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
815 	REQUIRE(dctx->use == DO_VERIFY);
816 
817 	if (key->key_alg == DST_ALG_ECDSA256) {
818 		siglen = DNS_SIG_ECDSA256SIZE;
819 	} else {
820 		siglen = DNS_SIG_ECDSA384SIZE;
821 	}
822 
823 	if (sig->length != siglen) {
824 		DST_RET(DST_R_VERIFYFAILURE);
825 	}
826 
827 	ecdsasig = ECDSA_SIG_new();
828 	if (ecdsasig == NULL) {
829 		DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
830 	}
831 	r = BN_bin2bn(cp, siglen / 2, NULL);
832 	cp += siglen / 2;
833 	s = BN_bin2bn(cp, siglen / 2, NULL);
834 	/* cp += siglen / 2; */
835 	ECDSA_SIG_set0(ecdsasig, r, s);
836 
837 	status = i2d_ECDSA_SIG(ecdsasig, NULL);
838 	if (status < 0) {
839 		DST_RET(dst__openssl_toresult3(dctx->category, "i2d_ECDSA_SIG",
840 					       DST_R_VERIFYFAILURE));
841 	}
842 
843 	sigder_len = (size_t)status;
844 	sigder = isc_mem_get(dctx->mctx, sigder_len);
845 	sigder_alloced = sigder_len;
846 
847 	sigder_copy = sigder;
848 	status = i2d_ECDSA_SIG(ecdsasig, &sigder_copy);
849 	if (status < 0) {
850 		DST_RET(dst__openssl_toresult3(dctx->category, "i2d_ECDSA_SIG",
851 					       DST_R_VERIFYFAILURE));
852 	}
853 
854 	status = EVP_DigestVerifyFinal(evp_md_ctx, sigder, sigder_len);
855 
856 	switch (status) {
857 	case 1:
858 		ret = ISC_R_SUCCESS;
859 		break;
860 	case 0:
861 		ret = dst__openssl_toresult(DST_R_VERIFYFAILURE);
862 		break;
863 	default:
864 		ret = dst__openssl_toresult3(dctx->category,
865 					     "EVP_DigestVerifyFinal",
866 					     DST_R_VERIFYFAILURE);
867 		break;
868 	}
869 
870 err:
871 	if (ecdsasig != NULL) {
872 		ECDSA_SIG_free(ecdsasig);
873 	}
874 	if (sigder != NULL && sigder_alloced != 0) {
875 		isc_mem_put(dctx->mctx, sigder, sigder_alloced);
876 	}
877 
878 	return ret;
879 }
880 
881 static isc_result_t
882 opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
883 	isc_result_t ret;
884 	EVP_PKEY *pkey = NULL;
885 
886 	REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
887 	UNUSED(unused);
888 	UNUSED(callback);
889 
890 	ret = opensslecdsa_generate_pkey(key->key_alg, key->label, &pkey);
891 	if (ret != ISC_R_SUCCESS) {
892 		return ret;
893 	}
894 
895 	key->key_size = EVP_PKEY_bits(pkey);
896 	key->keydata.pkeypair.priv = pkey;
897 	key->keydata.pkeypair.pub = pkey;
898 	return ret;
899 }
900 
901 static isc_result_t
902 opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) {
903 	isc_result_t ret;
904 	isc_region_t r;
905 	size_t keysize;
906 
907 	REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
908 	REQUIRE(key->keydata.pkeypair.pub != NULL);
909 
910 	keysize = opensslecdsa_key_alg_to_publickey_size(key->key_alg);
911 	isc_buffer_availableregion(data, &r);
912 	if (r.length < keysize) {
913 		DST_RET(ISC_R_NOSPACE);
914 	}
915 	if (!opensslecdsa_extract_public_key(key, r.base, keysize)) {
916 		DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
917 	}
918 
919 	isc_buffer_add(data, keysize);
920 	ret = ISC_R_SUCCESS;
921 
922 err:
923 	return ret;
924 }
925 
926 static isc_result_t
927 opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
928 	isc_result_t ret;
929 	EVP_PKEY *pkey = NULL;
930 	isc_region_t r;
931 	size_t len;
932 
933 	REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
934 	len = opensslecdsa_key_alg_to_publickey_size(key->key_alg);
935 
936 	isc_buffer_remainingregion(data, &r);
937 	if (r.length == 0) {
938 		DST_RET(ISC_R_SUCCESS);
939 	}
940 	if (r.length != len) {
941 		DST_RET(DST_R_INVALIDPUBLICKEY);
942 	}
943 
944 	ret = opensslecdsa_create_pkey(key->key_alg, false, r.base, len, &pkey);
945 	if (ret != ISC_R_SUCCESS) {
946 		DST_RET(ret);
947 	}
948 
949 	isc_buffer_forward(data, len);
950 	key->key_size = EVP_PKEY_bits(pkey);
951 	key->keydata.pkeypair.pub = pkey;
952 	ret = ISC_R_SUCCESS;
953 
954 err:
955 	return ret;
956 }
957 
958 static isc_result_t
959 opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
960 	isc_result_t ret;
961 	dst_private_t priv;
962 	unsigned char buf[MAX_PRIVKEY_SIZE];
963 	size_t keylen = 0;
964 	unsigned short i;
965 
966 	if (key->keydata.pkeypair.pub == NULL) {
967 		DST_RET(DST_R_NULLKEY);
968 	}
969 
970 	if (key->external) {
971 		priv.nelements = 0;
972 		DST_RET(dst__privstruct_writefile(key, &priv, directory));
973 	}
974 
975 	if (key->keydata.pkeypair.priv == NULL) {
976 		DST_RET(DST_R_NULLKEY);
977 	}
978 
979 	keylen = opensslecdsa_key_alg_to_publickey_size(key->key_alg) / 2;
980 	INSIST(keylen <= sizeof(buf));
981 
982 	i = 0;
983 	if (opensslecdsa_extract_private_key(key, buf, keylen)) {
984 		priv.elements[i].tag = TAG_ECDSA_PRIVATEKEY;
985 		priv.elements[i].length = keylen;
986 		priv.elements[i].data = buf;
987 		i++;
988 	}
989 	if (key->engine != NULL) {
990 		priv.elements[i].tag = TAG_ECDSA_ENGINE;
991 		priv.elements[i].length = (unsigned short)strlen(key->engine) +
992 					  1;
993 		priv.elements[i].data = (unsigned char *)key->engine;
994 		i++;
995 	}
996 
997 	if (key->label != NULL) {
998 		priv.elements[i].tag = TAG_ECDSA_LABEL;
999 		priv.elements[i].length = (unsigned short)strlen(key->label) +
1000 					  1;
1001 		priv.elements[i].data = (unsigned char *)key->label;
1002 		i++;
1003 	}
1004 
1005 	priv.nelements = i;
1006 	ret = dst__privstruct_writefile(key, &priv, directory);
1007 
1008 err:
1009 	isc_safe_memwipe(buf, keylen);
1010 	return ret;
1011 }
1012 
1013 static isc_result_t
1014 opensslecdsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
1015 		       const char *pin);
1016 
1017 static isc_result_t
1018 opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
1019 	dst_private_t priv;
1020 	isc_result_t ret;
1021 	EVP_PKEY *pkey = NULL;
1022 	const char *engine = NULL;
1023 	const char *label = NULL;
1024 	int i, privkey_index = -1;
1025 
1026 	REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
1027 
1028 	/* read private key file */
1029 	ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, key->mctx,
1030 				    &priv);
1031 	if (ret != ISC_R_SUCCESS) {
1032 		goto err;
1033 	}
1034 
1035 	if (key->external) {
1036 		if (priv.nelements != 0 || pub == NULL) {
1037 			DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY));
1038 		}
1039 		key->keydata.pkeypair.priv = pub->keydata.pkeypair.priv;
1040 		key->keydata.pkeypair.pub = pub->keydata.pkeypair.pub;
1041 		pub->keydata.pkeypair.priv = NULL;
1042 		pub->keydata.pkeypair.pub = NULL;
1043 		DST_RET(ISC_R_SUCCESS);
1044 	}
1045 
1046 	for (i = 0; i < priv.nelements; i++) {
1047 		switch (priv.elements[i].tag) {
1048 		case TAG_ECDSA_ENGINE:
1049 			engine = (char *)priv.elements[i].data;
1050 			break;
1051 		case TAG_ECDSA_LABEL:
1052 			label = (char *)priv.elements[i].data;
1053 			break;
1054 		case TAG_ECDSA_PRIVATEKEY:
1055 			privkey_index = i;
1056 			break;
1057 		default:
1058 			break;
1059 		}
1060 	}
1061 
1062 	if (label != NULL) {
1063 		ret = opensslecdsa_fromlabel(key, engine, label, NULL);
1064 		if (ret != ISC_R_SUCCESS) {
1065 			goto err;
1066 		}
1067 		/* Check that the public component matches if given */
1068 		if (pub != NULL && EVP_PKEY_eq(key->keydata.pkeypair.pub,
1069 					       pub->keydata.pkeypair.pub) != 1)
1070 		{
1071 			DST_RET(DST_R_INVALIDPRIVATEKEY);
1072 		}
1073 		DST_RET(ISC_R_SUCCESS);
1074 	}
1075 
1076 	if (privkey_index < 0) {
1077 		DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY));
1078 	}
1079 
1080 	ret = opensslecdsa_create_pkey(
1081 		key->key_alg, true, priv.elements[privkey_index].data,
1082 		priv.elements[privkey_index].length, &pkey);
1083 	if (ret != ISC_R_SUCCESS) {
1084 		goto err;
1085 	}
1086 
1087 	/* Check that the public component matches if given */
1088 	if (pub != NULL && EVP_PKEY_eq(pkey, pub->keydata.pkeypair.pub) != 1) {
1089 		DST_RET(DST_R_INVALIDPRIVATEKEY);
1090 	}
1091 
1092 	key->key_size = EVP_PKEY_bits(pkey);
1093 	key->keydata.pkeypair.priv = pkey;
1094 	key->keydata.pkeypair.pub = pkey;
1095 	pkey = NULL;
1096 
1097 err:
1098 	EVP_PKEY_free(pkey);
1099 	if (ret != ISC_R_SUCCESS) {
1100 		key->keydata.generic = NULL;
1101 	}
1102 	dst__privstruct_free(&priv, key->mctx);
1103 	isc_safe_memwipe(&priv, sizeof(priv));
1104 
1105 	return ret;
1106 }
1107 
1108 static isc_result_t
1109 opensslecdsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
1110 		       const char *pin) {
1111 	EVP_PKEY *privpkey = NULL, *pubpkey = NULL;
1112 	isc_result_t ret;
1113 
1114 	REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
1115 	UNUSED(pin);
1116 
1117 	ret = dst__openssl_fromlabel(EVP_PKEY_EC, engine, label, pin, &pubpkey,
1118 				     &privpkey);
1119 	if (ret != ISC_R_SUCCESS) {
1120 		goto err;
1121 	}
1122 
1123 	ret = opensslecdsa_validate_pkey_group(key->key_alg, privpkey);
1124 	if (ret != ISC_R_SUCCESS) {
1125 		goto err;
1126 	}
1127 	ret = opensslecdsa_validate_pkey_group(key->key_alg, pubpkey);
1128 	if (ret != ISC_R_SUCCESS) {
1129 		goto err;
1130 	}
1131 
1132 	if (engine != NULL) {
1133 		key->engine = isc_mem_strdup(key->mctx, engine);
1134 	}
1135 	key->label = isc_mem_strdup(key->mctx, label);
1136 	key->key_size = EVP_PKEY_bits(privpkey);
1137 	key->keydata.pkeypair.priv = privpkey;
1138 	key->keydata.pkeypair.pub = pubpkey;
1139 	privpkey = NULL;
1140 	pubpkey = NULL;
1141 
1142 err:
1143 	EVP_PKEY_free(privpkey);
1144 	EVP_PKEY_free(pubpkey);
1145 	return ret;
1146 }
1147 
1148 static dst_func_t opensslecdsa_functions = {
1149 	opensslecdsa_createctx,
1150 	NULL, /*%< createctx2 */
1151 	opensslecdsa_destroyctx,
1152 	opensslecdsa_adddata,
1153 	opensslecdsa_sign,
1154 	opensslecdsa_verify,
1155 	NULL, /*%< verify2 */
1156 	NULL, /*%< computesecret */
1157 	dst__openssl_keypair_compare,
1158 	NULL, /*%< paramcompare */
1159 	opensslecdsa_generate,
1160 	dst__openssl_keypair_isprivate,
1161 	dst__openssl_keypair_destroy,
1162 	opensslecdsa_todns,
1163 	opensslecdsa_fromdns,
1164 	opensslecdsa_tofile,
1165 	opensslecdsa_parse,
1166 	NULL,			/*%< cleanup */
1167 	opensslecdsa_fromlabel, /*%< fromlabel */
1168 	NULL,			/*%< dump */
1169 	NULL,			/*%< restore */
1170 };
1171 
1172 isc_result_t
1173 dst__opensslecdsa_init(dst_func_t **funcp) {
1174 	REQUIRE(funcp != NULL);
1175 	if (*funcp == NULL) {
1176 		*funcp = &opensslecdsa_functions;
1177 	}
1178 	return ISC_R_SUCCESS;
1179 }
1180