1 /*	$NetBSD: opensslgost_link.c,v 1.10 2015/07/08 17:28:58 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2010-2014  Internet Systems Consortium, Inc. ("ISC")
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /* Id: opensslgost_link.c,v 1.5 2011/01/19 23:47:12 tbox Exp  */
20 
21 #include <config.h>
22 
23 #if defined(OPENSSL) && defined(HAVE_OPENSSL_GOST)
24 
25 #include <isc/entropy.h>
26 #include <isc/mem.h>
27 #include <isc/string.h>
28 #include <isc/util.h>
29 
30 #include <dst/result.h>
31 
32 #include "dst_internal.h"
33 #include "dst_openssl.h"
34 #include "dst_parse.h"
35 #include "dst_gost.h"
36 
37 #include <openssl/err.h>
38 #include <openssl/objects.h>
39 #include <openssl/rsa.h>
40 #include <openssl/engine.h>
41 
42 static ENGINE *e = NULL;
43 static const EVP_MD *opensslgost_digest;
44 extern const EVP_MD *EVP_gost(void);
45 
46 const EVP_MD *EVP_gost(void) {
47 	return (opensslgost_digest);
48 }
49 
50 /* ISC methods */
51 
52 isc_result_t
53 isc_gost_init(isc_gost_t *ctx) {
54 	const EVP_MD *md;
55 	int ret;
56 
57 	INSIST(ctx != NULL);
58 
59 	md = EVP_gost();
60 	if (md == NULL)
61 		return (DST_R_CRYPTOFAILURE);
62 	EVP_MD_CTX_init(ctx);
63 	ret = EVP_DigestInit(ctx, md);
64 	if (ret != 1)
65 		return (DST_R_CRYPTOFAILURE);
66 	return (ISC_R_SUCCESS);
67 }
68 
69 void
70 isc_gost_invalidate(isc_gost_t *ctx) {
71 	EVP_MD_CTX_cleanup(ctx);
72 }
73 
74 isc_result_t
75 isc_gost_update(isc_gost_t *ctx, const unsigned char *data,
76 		unsigned int len)
77 {
78 	int ret;
79 
80 	INSIST(ctx != NULL);
81 	INSIST(data != NULL);
82 
83 	ret = EVP_DigestUpdate(ctx, (const void *) data, (size_t) len);
84 	if (ret != 1)
85 		return (DST_R_CRYPTOFAILURE);
86 	return (ISC_R_SUCCESS);
87 }
88 
89 isc_result_t
90 isc_gost_final(isc_gost_t *ctx, unsigned char *digest) {
91 	int ret;
92 
93 	INSIST(ctx != NULL);
94 	INSIST(digest != NULL);
95 
96 	ret = EVP_DigestFinal(ctx, digest, NULL);
97 	if (ret != 1)
98 		return (DST_R_CRYPTOFAILURE);
99 	return (ISC_R_SUCCESS);
100 }
101 
102 /* DST methods */
103 
104 #define DST_RET(a) {ret = a; goto err;}
105 
106 static isc_result_t opensslgost_todns(const dst_key_t *key,
107 				      isc_buffer_t *data);
108 
109 static isc_result_t
110 opensslgost_createctx(dst_key_t *key, dst_context_t *dctx) {
111 	EVP_MD_CTX *evp_md_ctx;
112 	const EVP_MD *md = EVP_gost();
113 
114 	UNUSED(key);
115 
116 	if (md == NULL)
117 		return (DST_R_OPENSSLFAILURE);
118 
119 	evp_md_ctx = EVP_MD_CTX_create();
120 	if (evp_md_ctx == NULL)
121 		return (ISC_R_NOMEMORY);
122 
123 	if (!EVP_DigestInit_ex(evp_md_ctx, md, NULL)) {
124 		EVP_MD_CTX_destroy(evp_md_ctx);
125 		return (ISC_R_FAILURE);
126 	}
127 	dctx->ctxdata.evp_md_ctx = evp_md_ctx;
128 
129 	return (ISC_R_SUCCESS);
130 }
131 
132 static void
133 opensslgost_destroyctx(dst_context_t *dctx) {
134 	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
135 
136 	if (evp_md_ctx != NULL) {
137 		EVP_MD_CTX_destroy(evp_md_ctx);
138 		dctx->ctxdata.evp_md_ctx = NULL;
139 	}
140 }
141 
142 static isc_result_t
143 opensslgost_adddata(dst_context_t *dctx, const isc_region_t *data) {
144 	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
145 
146 	if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length))
147 		return (ISC_R_FAILURE);
148 
149 	return (ISC_R_SUCCESS);
150 }
151 
152 static isc_result_t
153 opensslgost_sign(dst_context_t *dctx, isc_buffer_t *sig) {
154 	dst_key_t *key = dctx->key;
155 	isc_region_t r;
156 	unsigned int siglen = 0;
157 	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
158 	EVP_PKEY *pkey = key->keydata.pkey;
159 
160 	isc_buffer_availableregion(sig, &r);
161 
162 	if (r.length < (unsigned int) EVP_PKEY_size(pkey))
163 		return (ISC_R_NOSPACE);
164 
165 	if (!EVP_SignFinal(evp_md_ctx, r.base, &siglen, pkey))
166 		return (ISC_R_FAILURE);
167 
168 	isc_buffer_add(sig, siglen);
169 
170 	return (ISC_R_SUCCESS);
171 }
172 
173 static isc_result_t
174 opensslgost_verify(dst_context_t *dctx, const isc_region_t *sig) {
175 	dst_key_t *key = dctx->key;
176 	int status = 0;
177 	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
178 	EVP_PKEY *pkey = key->keydata.pkey;
179 
180 	status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey);
181 	switch (status) {
182 	case 1:
183 		return (ISC_R_SUCCESS);
184 	case 0:
185 		return (dst__openssl_toresult(DST_R_VERIFYFAILURE));
186 	default:
187 		return (dst__openssl_toresult3(dctx->category,
188 					       "EVP_VerifyFinal",
189 					       DST_R_VERIFYFAILURE));
190 	}
191 }
192 
193 static isc_boolean_t
194 opensslgost_compare(const dst_key_t *key1, const dst_key_t *key2) {
195 	EVP_PKEY *pkey1, *pkey2;
196 
197 	pkey1 = key1->keydata.pkey;
198 	pkey2 = key2->keydata.pkey;
199 
200 	if (pkey1 == NULL && pkey2 == NULL)
201 		return (ISC_TRUE);
202 	else if (pkey1 == NULL || pkey2 == NULL)
203 		return (ISC_FALSE);
204 
205 	if (EVP_PKEY_cmp(pkey1, pkey2) != 1)
206 		return (ISC_FALSE);
207 	return (ISC_TRUE);
208 }
209 
210 static int
211 progress_cb(EVP_PKEY_CTX *ctx)
212 {
213 	union {
214 		void *dptr;
215 		void (*fptr)(int);
216 	} u;
217 	int p;
218 
219 	u.dptr = EVP_PKEY_CTX_get_app_data(ctx);
220 	p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
221 	if (u.fptr != NULL)
222 		u.fptr(p);
223 	return (1);
224 }
225 
226 static isc_result_t
227 opensslgost_generate(dst_key_t *key, int unused, void (*callback)(int)) {
228 	EVP_PKEY_CTX *ctx;
229 	union {
230 		void *dptr;
231 		void (*fptr)(int);
232 	} u;
233 	EVP_PKEY *pkey = NULL;
234 	isc_result_t ret;
235 
236 	UNUSED(unused);
237 	ctx = EVP_PKEY_CTX_new_id(NID_id_GostR3410_2001, NULL);
238 	if (ctx == NULL)
239 		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_id",
240 					       DST_R_OPENSSLFAILURE));
241 	if (callback != NULL) {
242 		u.fptr = callback;
243 		EVP_PKEY_CTX_set_app_data(ctx, u.dptr);
244 		EVP_PKEY_CTX_set_cb(ctx, &progress_cb);
245 	}
246 	if (EVP_PKEY_keygen_init(ctx) <= 0)
247 		DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init",
248 					       DST_R_OPENSSLFAILURE));
249 	if (EVP_PKEY_CTX_ctrl_str(ctx, "paramset", "A") <= 0)
250 		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_ctrl_str",
251 					       DST_R_OPENSSLFAILURE));
252 	if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
253 		DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen",
254 					       DST_R_OPENSSLFAILURE));
255 	key->keydata.pkey = pkey;
256 	key->key_size = EVP_PKEY_bits(pkey);
257 	EVP_PKEY_CTX_free(ctx);
258 	return (ISC_R_SUCCESS);
259 
260 err:
261 	if (pkey != NULL)
262 		EVP_PKEY_free(pkey);
263 	if (ctx != NULL)
264 		EVP_PKEY_CTX_free(ctx);
265 	return (ret);
266 }
267 
268 static isc_boolean_t
269 opensslgost_isprivate(const dst_key_t *key) {
270 	EVP_PKEY *pkey = key->keydata.pkey;
271 	EC_KEY *ec;
272 
273 	INSIST(pkey != NULL);
274 
275 	ec = EVP_PKEY_get0(pkey);
276 	return (ISC_TF(ec != NULL && EC_KEY_get0_private_key(ec) != NULL));
277 }
278 
279 static void
280 opensslgost_destroy(dst_key_t *key) {
281 	EVP_PKEY *pkey = key->keydata.pkey;
282 
283 	EVP_PKEY_free(pkey);
284 	key->keydata.pkey = NULL;
285 }
286 
287 unsigned char gost_prefix[37] = {
288 	0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85,
289 	0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07,
290 	0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01, 0x06,
291 	0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01,
292 	0x03, 0x43, 0x00, 0x04, 0x40
293 };
294 
295 static isc_result_t
296 opensslgost_todns(const dst_key_t *key, isc_buffer_t *data) {
297 	EVP_PKEY *pkey;
298 	isc_region_t r;
299 	unsigned char der[37 + 64], *p;
300 	int len;
301 
302 	REQUIRE(key->keydata.pkey != NULL);
303 
304 	pkey = key->keydata.pkey;
305 
306 	isc_buffer_availableregion(data, &r);
307 	if (r.length < 64)
308 		return (ISC_R_NOSPACE);
309 
310 	p = der;
311 	len = i2d_PUBKEY(pkey, &p);
312 	INSIST(len == sizeof(der));
313 	INSIST(memcmp(gost_prefix, der, 37) == 0);
314 	memmove(r.base, der + 37, 64);
315 	isc_buffer_add(data, 64);
316 
317 	return (ISC_R_SUCCESS);
318 }
319 
320 static isc_result_t
321 opensslgost_fromdns(dst_key_t *key, isc_buffer_t *data) {
322 	isc_region_t r;
323 	EVP_PKEY *pkey = NULL;
324 	unsigned char der[37 + 64];
325 	const unsigned char *p;
326 
327 	isc_buffer_remainingregion(data, &r);
328 	if (r.length == 0)
329 		return (ISC_R_SUCCESS);
330 
331 	if (r.length != 64)
332 		return (DST_R_INVALIDPUBLICKEY);
333 	memmove(der, gost_prefix, 37);
334 	memmove(der + 37, r.base, 64);
335 	isc_buffer_forward(data, 64);
336 
337 	p = der;
338 	if (d2i_PUBKEY(&pkey, &p, (long) sizeof(der)) == NULL)
339 		return (dst__openssl_toresult2("d2i_PUBKEY",
340 					       DST_R_OPENSSLFAILURE));
341 	key->keydata.pkey = pkey;
342 	key->key_size = EVP_PKEY_bits(pkey);
343 
344 	return (ISC_R_SUCCESS);
345 }
346 
347 #ifdef PREFER_GOSTASN1
348 
349 static isc_result_t
350 opensslgost_tofile(const dst_key_t *key, const char *directory) {
351 	EVP_PKEY *pkey;
352 	dst_private_t priv;
353 	isc_result_t result;
354 	unsigned char *der, *p;
355 	int len;
356 
357 	if (key->keydata.pkey == NULL)
358 		return (DST_R_NULLKEY);
359 
360 	if (key->external) {
361 		priv.nelements = 0;
362 		return (dst__privstruct_writefile(key, &priv, directory));
363 	}
364 
365 	pkey = key->keydata.pkey;
366 
367 	len = i2d_PrivateKey(pkey, NULL);
368 	der = isc_mem_get(key->mctx, (size_t) len);
369 	if (der == NULL)
370 		return (ISC_R_NOMEMORY);
371 
372 	p = der;
373 	if (i2d_PrivateKey(pkey, &p) != len) {
374 		result = dst__openssl_toresult2("i2d_PrivateKey",
375 						DST_R_OPENSSLFAILURE);
376 		goto fail;
377 	}
378 
379 	priv.elements[0].tag = TAG_GOST_PRIVASN1;
380 	priv.elements[0].length = len;
381 	priv.elements[0].data = der;
382 	priv.nelements = 1;
383 
384 	result = dst__privstruct_writefile(key, &priv, directory);
385  fail:
386 	if (der != NULL)
387 		isc_mem_put(key->mctx, der, (size_t) len);
388 	return (result);
389 }
390 
391 #else
392 
393 static isc_result_t
394 opensslgost_tofile(const dst_key_t *key, const char *directory) {
395 	EVP_PKEY *pkey;
396 	EC_KEY *eckey;
397 	const BIGNUM *privkey;
398 	dst_private_t priv;
399 	isc_result_t ret;
400 	unsigned char *buf = NULL;
401 
402 	if (key->keydata.pkey == NULL)
403 		return (DST_R_NULLKEY);
404 
405 	if (key->external) {
406 		priv.nelements = 0;
407 		return (dst__privstruct_writefile(key, &priv, directory));
408 	}
409 
410 	pkey = key->keydata.pkey;
411 	eckey = EVP_PKEY_get0(pkey);
412 	if (eckey == NULL)
413 		return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
414 	privkey = EC_KEY_get0_private_key(eckey);
415 	if (privkey == NULL)
416 		return (ISC_R_FAILURE);
417 
418 	buf = isc_mem_get(key->mctx, BN_num_bytes(privkey));
419 	if (buf == NULL)
420 		return (ISC_R_NOMEMORY);
421 
422 	priv.elements[0].tag = TAG_GOST_PRIVRAW;
423 	priv.elements[0].length = BN_num_bytes(privkey);
424 	BN_bn2bin(privkey, buf);
425 	priv.elements[0].data = buf;
426 	priv.nelements = 1;
427 
428 	ret = dst__privstruct_writefile(key, &priv, directory);
429 
430 	if (buf != NULL)
431 		isc_mem_put(key->mctx, buf, BN_num_bytes(privkey));
432 	return (ret);
433 }
434 #endif
435 
436 static unsigned char gost_dummy_key[71] = {
437 	0x30, 0x45, 0x02, 0x01, 0x00, 0x30, 0x1c, 0x06,
438 	0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30,
439 	0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02,
440 	0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02,
441 	0x02, 0x1e, 0x01, 0x04, 0x22, 0x02, 0x20, 0x1b,
442 	0x3f, 0x94, 0xf7, 0x1a, 0x5f, 0x2f, 0xe7, 0xe5,
443 	0x74, 0x0b, 0x8c, 0xd4, 0xb7, 0x18, 0xdd, 0x65,
444 	0x68, 0x26, 0xd1, 0x54, 0xfb, 0x77, 0xba, 0x63,
445 	0x72, 0xd9, 0xf0, 0x63, 0x87, 0xe0, 0xd6
446 };
447 
448 static isc_result_t
449 opensslgost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
450 	dst_private_t priv;
451 	isc_result_t ret;
452 	isc_mem_t *mctx = key->mctx;
453 	EVP_PKEY *pkey = NULL;
454 	EC_KEY *eckey;
455 	const EC_POINT *pubkey = NULL;
456 	BIGNUM *privkey = NULL;
457 	const unsigned char *p;
458 
459 	/* read private key file */
460 	ret = dst__privstruct_parse(key, DST_ALG_ECCGOST, lexer, mctx, &priv);
461 	if (ret != ISC_R_SUCCESS)
462 		return (ret);
463 
464 	if (key->external) {
465 		if (priv.nelements != 0)
466 			DST_RET(DST_R_INVALIDPRIVATEKEY);
467 		if (pub == NULL)
468 			DST_RET(DST_R_INVALIDPRIVATEKEY);
469 		key->keydata.pkey = pub->keydata.pkey;
470 		pub->keydata.pkey = NULL;
471 		key->key_size = pub->key_size;
472 		dst__privstruct_free(&priv, mctx);
473 		memset(&priv, 0, sizeof(priv));
474 		return (ISC_R_SUCCESS);
475 	}
476 
477 	INSIST((priv.elements[0].tag == TAG_GOST_PRIVASN1) ||
478 	       (priv.elements[0].tag == TAG_GOST_PRIVRAW));
479 
480 	if (priv.elements[0].tag == TAG_GOST_PRIVASN1) {
481 		p = priv.elements[0].data;
482 		if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
483 				   (long) priv.elements[0].length) == NULL)
484 			DST_RET(dst__openssl_toresult2(
485 					    "d2i_PrivateKey",
486 					    DST_R_INVALIDPRIVATEKEY));
487 	} else {
488 		if ((pub != NULL) && (pub->keydata.pkey != NULL)) {
489 			eckey = EVP_PKEY_get0(pub->keydata.pkey);
490 			pubkey = EC_KEY_get0_public_key(eckey);
491 		}
492 
493 		privkey = BN_bin2bn(priv.elements[0].data,
494 				    priv.elements[0].length, NULL);
495 		if (privkey == NULL)
496 			DST_RET(ISC_R_NOMEMORY);
497 
498 		/* can't create directly the whole key */
499 		p = gost_dummy_key;
500 		if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
501 				   (long) sizeof(gost_dummy_key)) == NULL)
502 			DST_RET(dst__openssl_toresult2(
503 					    "d2i_PrivateKey",
504 					    DST_R_INVALIDPRIVATEKEY));
505 
506 		eckey = EVP_PKEY_get0(pkey);
507 		if (eckey == NULL)
508 			return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
509 		if (!EC_KEY_set_private_key(eckey, privkey))
510 			DST_RET(ISC_R_NOMEMORY);
511 
512 		/* have to (re)set the public key */
513 #ifdef notyet
514 		(void) gost2001_compute_public(eckey);
515 #else
516 		if ((pubkey != NULL) && !EC_KEY_set_public_key(eckey, pubkey))
517 			DST_RET(ISC_R_NOMEMORY);
518 #endif
519 		BN_clear_free(privkey);
520 		privkey = NULL;
521 	}
522 	key->keydata.pkey = pkey;
523 	key->key_size = EVP_PKEY_bits(pkey);
524 	dst__privstruct_free(&priv, mctx);
525 	memset(&priv, 0, sizeof(priv));
526 	return (ISC_R_SUCCESS);
527 
528  err:
529 	if (privkey != NULL)
530 		BN_clear_free(privkey);
531 	if (pkey != NULL)
532 		EVP_PKEY_free(pkey);
533 	opensslgost_destroy(key);
534 	dst__privstruct_free(&priv, mctx);
535 	memset(&priv, 0, sizeof(priv));
536 	return (ret);
537 }
538 
539 static void
540 opensslgost_cleanup(void) {
541 	if (e != NULL) {
542 		ENGINE_finish(e);
543 		ENGINE_free(e);
544 		e = NULL;
545 	}
546 }
547 
548 static dst_func_t opensslgost_functions = {
549 	opensslgost_createctx,
550 	NULL, /*%< createctx2 */
551 	opensslgost_destroyctx,
552 	opensslgost_adddata,
553 	opensslgost_sign,
554 	opensslgost_verify,
555 	NULL, /*%< verify2 */
556 	NULL, /*%< computesecret */
557 	opensslgost_compare,
558 	NULL, /*%< paramcompare */
559 	opensslgost_generate,
560 	opensslgost_isprivate,
561 	opensslgost_destroy,
562 	opensslgost_todns,
563 	opensslgost_fromdns,
564 	opensslgost_tofile,
565 	opensslgost_parse,
566 	opensslgost_cleanup,
567 	NULL, /*%< fromlabel */
568 	NULL, /*%< dump */
569 	NULL  /*%< restore */
570 };
571 
572 isc_result_t
573 dst__opensslgost_init(dst_func_t **funcp) {
574 	isc_result_t ret;
575 
576 	REQUIRE(funcp != NULL);
577 
578 	/* check if the gost engine works properly */
579 	e = ENGINE_by_id("gost");
580 	if (e == NULL)
581 		return (dst__openssl_toresult2("ENGINE_by_id",
582 					       DST_R_OPENSSLFAILURE));
583 	if (ENGINE_init(e) <= 0) {
584 		ENGINE_free(e);
585 		e = NULL;
586 		return (dst__openssl_toresult2("ENGINE_init",
587 					       DST_R_OPENSSLFAILURE));
588 	}
589 	/* better than to rely on digest_gost symbol */
590 	opensslgost_digest = ENGINE_get_digest(e, NID_id_GostR3411_94);
591 	if (opensslgost_digest == NULL)
592 		DST_RET(dst__openssl_toresult2("ENGINE_get_digest",
593 					       DST_R_OPENSSLFAILURE));
594 	/* from openssl.cnf */
595 	if (ENGINE_register_pkey_asn1_meths(e) <= 0)
596 		DST_RET(dst__openssl_toresult2(
597 				"ENGINE_register_pkey_asn1_meths",
598 				DST_R_OPENSSLFAILURE));
599 	if (ENGINE_ctrl_cmd_string(e,
600 				   "CRYPT_PARAMS",
601 				   "id-Gost28147-89-CryptoPro-A-ParamSet",
602 				   0) <= 0)
603 		DST_RET(dst__openssl_toresult2("ENGINE_ctrl_cmd_string",
604 					       DST_R_OPENSSLFAILURE));
605 
606 	if (*funcp == NULL)
607 		*funcp = &opensslgost_functions;
608 	return (ISC_R_SUCCESS);
609 
610  err:
611 	ENGINE_finish(e);
612 	ENGINE_free(e);
613 	e = NULL;
614 	return (ret);
615 }
616 
617 #else /* HAVE_OPENSSL_GOST */
618 
619 #include <isc/util.h>
620 
621 EMPTY_TRANSLATION_UNIT
622 
623 #endif /* HAVE_OPENSSL_GOST */
624 /*! \file */
625