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
EVP_gost(void)46 const EVP_MD *EVP_gost(void) {
47 return (opensslgost_digest);
48 }
49
50 /* ISC methods */
51
52 isc_result_t
isc_gost_init(isc_gost_t * ctx)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
isc_gost_invalidate(isc_gost_t * ctx)70 isc_gost_invalidate(isc_gost_t *ctx) {
71 EVP_MD_CTX_cleanup(ctx);
72 }
73
74 isc_result_t
isc_gost_update(isc_gost_t * ctx,const unsigned char * data,unsigned int len)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
isc_gost_final(isc_gost_t * ctx,unsigned char * digest)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
opensslgost_createctx(dst_key_t * key,dst_context_t * dctx)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
opensslgost_destroyctx(dst_context_t * dctx)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
opensslgost_adddata(dst_context_t * dctx,const isc_region_t * data)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
opensslgost_sign(dst_context_t * dctx,isc_buffer_t * sig)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
opensslgost_verify(dst_context_t * dctx,const isc_region_t * sig)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
opensslgost_compare(const dst_key_t * key1,const dst_key_t * key2)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
progress_cb(EVP_PKEY_CTX * ctx)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
opensslgost_generate(dst_key_t * key,int unused,void (* callback)(int))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
opensslgost_isprivate(const dst_key_t * key)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
opensslgost_destroy(dst_key_t * key)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
opensslgost_todns(const dst_key_t * key,isc_buffer_t * data)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
opensslgost_fromdns(dst_key_t * key,isc_buffer_t * data)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
opensslgost_tofile(const dst_key_t * key,const char * directory)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
opensslgost_tofile(const dst_key_t * key,const char * directory)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
opensslgost_parse(dst_key_t * key,isc_lex_t * lexer,dst_key_t * pub)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
opensslgost_cleanup(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
dst__opensslgost_init(dst_func_t ** funcp)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