1119418Sobrien/*
291398Stmm * Copyright (C) 2010-2014  Internet Systems Consortium, Inc. ("ISC")
3108832Stmm *
4174987Smarius * Permission to use, copy, modify, and/or distribute this software for any
591398Stmm * purpose with or without fee is hereby granted, provided that the above
691398Stmm * copyright notice and this permission notice appear in all copies.
791398Stmm *
891398Stmm * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
991398Stmm * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1091398Stmm * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
1191398Stmm * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1291398Stmm * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
1391398Stmm * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1491398Stmm * PERFORMANCE OF THIS SOFTWARE.
1591398Stmm */
1691398Stmm
1791398Stmm/* $Id: opensslgost_link.c,v 1.5 2011/01/19 23:47:12 tbox Exp $ */
1891398Stmm
1991398Stmm#include <config.h>
2091398Stmm
2191398Stmm#ifdef HAVE_OPENSSL_GOST
2291398Stmm
2391398Stmm#include <isc/entropy.h>
2491398Stmm#include <isc/mem.h>
2591398Stmm#include <isc/string.h>
2691398Stmm#include <isc/util.h>
2791398Stmm
2899726Sbenno#include <dst/result.h>
2991398Stmm
3091398Stmm#include "dst_internal.h"
31119418Sobrien#include "dst_openssl.h"
32119418Sobrien#include "dst_parse.h"
33119418Sobrien
3491398Stmm#include <openssl/err.h>
35172334Smarius#include <openssl/objects.h>
3691398Stmm#include <openssl/rsa.h>
3791398Stmm#include <openssl/engine.h>
38115030Stmm
3991398Stmmstatic ENGINE *e = NULL;
40115030Stmmstatic const EVP_MD *opensslgost_digest;
4191398Stmmextern const EVP_MD *EVP_gost(void);
42148368Smarius
43148368Smariusconst EVP_MD *EVP_gost(void) {
44148368Smarius	return (opensslgost_digest);
45148368Smarius}
4691398Stmm
4791398Stmm#define DST_RET(a) {ret = a; goto err;}
4891398Stmm
4991398Stmmstatic isc_result_t opensslgost_todns(const dst_key_t *key,
5095533Smike				      isc_buffer_t *data);
5191398Stmm
5291398Stmmstatic isc_result_t
5391398Stmmopensslgost_createctx(dst_key_t *key, dst_context_t *dctx) {
54148369Smarius	EVP_MD_CTX *evp_md_ctx;
55130026Sphk	const EVP_MD *md = EVP_gost();
56148369Smarius
5791398Stmm	UNUSED(key);
5891398Stmm
59169269Sphk	if (md == NULL)
6091398Stmm		return (DST_R_OPENSSLFAILURE);
61105982Stmm
6291398Stmm	evp_md_ctx = EVP_MD_CTX_create();
6391398Stmm	if (evp_md_ctx == NULL)
6491398Stmm		return (ISC_R_NOMEMORY);
6591398Stmm
6691398Stmm	if (!EVP_DigestInit_ex(evp_md_ctx, md, NULL)) {
67147256Sbrooks		EVP_MD_CTX_destroy(evp_md_ctx);
68149552Smarius		return (ISC_R_FAILURE);
6991398Stmm	}
70170273Syongari	dctx->ctxdata.evp_md_ctx = evp_md_ctx;
71170273Syongari
72170273Syongari	return (ISC_R_SUCCESS);
73170273Syongari}
74170273Syongari
75170273Syongaristatic void
7691398Stmmopensslgost_destroyctx(dst_context_t *dctx) {
7791398Stmm	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
7891398Stmm
7991398Stmm	if (evp_md_ctx != NULL) {
8091398Stmm		EVP_MD_CTX_destroy(evp_md_ctx);
81119355Simp		dctx->ctxdata.evp_md_ctx = NULL;
82119355Simp	}
8391398Stmm}
84172334Smarius
85172334Smariusstatic isc_result_t
86172334Smariusopensslgost_adddata(dst_context_t *dctx, const isc_region_t *data) {
87194763Smarius	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
88172334Smarius
89170273Syongari	if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length))
90182060Smarius		return (ISC_R_FAILURE);
91170273Syongari
92174987Smarius	return (ISC_R_SUCCESS);
93174987Smarius}
94170273Syongari
95170273Syongaristatic isc_result_t
96170273Syongariopensslgost_sign(dst_context_t *dctx, isc_buffer_t *sig) {
9791398Stmm	dst_key_t *key = dctx->key;
98174987Smarius	isc_region_t r;
99177560Smarius	unsigned int siglen = 0;
100177560Smarius	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
101174987Smarius	EVP_PKEY *pkey = key->keydata.pkey;
102174987Smarius
103174987Smarius	isc_buffer_availableregion(sig, &r);
104174987Smarius
105174987Smarius	if (r.length < (unsigned int) EVP_PKEY_size(pkey))
106174987Smarius		return (ISC_R_NOSPACE);
107174987Smarius
108174987Smarius	if (!EVP_SignFinal(evp_md_ctx, r.base, &siglen, pkey))
109174987Smarius		return (ISC_R_FAILURE);
110174987Smarius
111174987Smarius	isc_buffer_add(sig, siglen);
112174987Smarius
113174987Smarius	return (ISC_R_SUCCESS);
114174987Smarius}
115172334Smarius
116174987Smariusstatic isc_result_t
117174987Smariusopensslgost_verify(dst_context_t *dctx, const isc_region_t *sig) {
118174987Smarius	dst_key_t *key = dctx->key;
119148368Smarius	int status = 0;
120174987Smarius	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
121100587Sjake	EVP_PKEY *pkey = key->keydata.pkey;
122194763Smarius
123174987Smarius	status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey);
124174987Smarius	switch (status) {
125174987Smarius	case 1:
126174987Smarius		return (ISC_R_SUCCESS);
127174987Smarius	case 0:
128174987Smarius		return (dst__openssl_toresult(DST_R_VERIFYFAILURE));
129174987Smarius	default:
130194763Smarius		return (dst__openssl_toresult3(dctx->category,
131174987Smarius					       "EVP_VerifyFinal",
13291398Stmm					       DST_R_VERIFYFAILURE));
13391398Stmm	}
13491398Stmm}
13591398Stmm
13691398Stmmstatic isc_boolean_t
13791398Stmmopensslgost_compare(const dst_key_t *key1, const dst_key_t *key2) {
13891398Stmm	EVP_PKEY *pkey1, *pkey2;
13991398Stmm
14091398Stmm	pkey1 = key1->keydata.pkey;
14191398Stmm	pkey2 = key2->keydata.pkey;
142177560Smarius
143177560Smarius	if (pkey1 == NULL && pkey2 == NULL)
144177560Smarius		return (ISC_TRUE);
145177560Smarius	else if (pkey1 == NULL || pkey2 == NULL)
146177560Smarius		return (ISC_FALSE);
14791398Stmm
148174987Smarius	if (EVP_PKEY_cmp(pkey1, pkey2) != 1)
14991398Stmm		return (ISC_FALSE);
150174987Smarius	return (ISC_TRUE);
151147256Sbrooks}
152174987Smarius
153174987Smariusstatic int
15491398Stmmprogress_cb(EVP_PKEY_CTX *ctx)
155194763Smarius{
156194763Smarius	union {
157194763Smarius		void *dptr;
158194763Smarius		void (*fptr)(int);
159147256Sbrooks	} u;
160147256Sbrooks	int p;
161147256Sbrooks
162194763Smarius	u.dptr = EVP_PKEY_CTX_get_app_data(ctx);
163194763Smarius	p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
164194763Smarius	if (u.fptr != NULL)
165194763Smarius		u.fptr(p);
166194763Smarius	return (1);
167194763Smarius}
168194763Smarius
169194763Smariusstatic isc_result_t
170194763Smariusopensslgost_generate(dst_key_t *key, int unused, void (*callback)(int)) {
171194763Smarius	EVP_PKEY_CTX *ctx;
172194763Smarius	union {
173147256Sbrooks		void *dptr;
174150285Smarius		void (*fptr)(int);
175150285Smarius	} u;
176150285Smarius	EVP_PKEY *pkey = NULL;
177150285Smarius	isc_result_t ret;
178150285Smarius
17991398Stmm	UNUSED(unused);
18091398Stmm	ctx = EVP_PKEY_CTX_new_id(NID_id_GostR3410_2001, NULL);
18191398Stmm	if (ctx == NULL)
182161928Sjmg		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_id",
183161928Sjmg					       DST_R_OPENSSLFAILURE));
184174987Smarius	if (callback != NULL) {
185174987Smarius		u.fptr = callback;
186194763Smarius		EVP_PKEY_CTX_set_app_data(ctx, u.dptr);
187147256Sbrooks		EVP_PKEY_CTX_set_cb(ctx, &progress_cb);
18891398Stmm	}
18991398Stmm	if (EVP_PKEY_keygen_init(ctx) <= 0)
190170273Syongari		DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init",
191170273Syongari					       DST_R_OPENSSLFAILURE));
192194763Smarius	if (EVP_PKEY_CTX_ctrl_str(ctx, "paramset", "A") <= 0)
193108832Stmm		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_ctrl_str",
19491398Stmm					       DST_R_OPENSSLFAILURE));
195108832Stmm	if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
196170273Syongari		DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen",
197170273Syongari					       DST_R_OPENSSLFAILURE));
198117126Sscottl	key->keydata.pkey = pkey;
199194763Smarius	EVP_PKEY_CTX_free(ctx);
200108832Stmm	return (ISC_R_SUCCESS);
201108832Stmm
20291398Stmmerr:
203170273Syongari	if (pkey != NULL)
20491398Stmm		EVP_PKEY_free(pkey);
205170273Syongari	if (ctx != NULL)
206170273Syongari		EVP_PKEY_CTX_free(ctx);
207194763Smarius	return (ret);
208108832Stmm}
20991398Stmm
21091398Stmmstatic isc_boolean_t
211174987Smariusopensslgost_isprivate(const dst_key_t *key) {
21291398Stmm	EVP_PKEY *pkey = key->keydata.pkey;
21391398Stmm	EC_KEY *ec;
21491398Stmm
215170273Syongari	INSIST(pkey != NULL);
216170273Syongari
217194763Smarius	ec = EVP_PKEY_get0(pkey);
218174987Smarius	return (ISC_TF(ec != NULL && EC_KEY_get0_private_key(ec) != NULL));
219174987Smarius}
220108832Stmm
22191398Stmmstatic void
22291398Stmmopensslgost_destroy(dst_key_t *key) {
22391398Stmm	EVP_PKEY *pkey = key->keydata.pkey;
22491398Stmm
22591398Stmm	EVP_PKEY_free(pkey);
22691398Stmm	key->keydata.pkey = NULL;
227174987Smarius}
228174987Smarius
229174987Smariusunsigned char gost_prefix[37] = {
230108832Stmm	0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85,
23191398Stmm	0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07,
23291398Stmm	0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01, 0x06,
23391398Stmm	0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01,
23491398Stmm	0x03, 0x43, 0x00, 0x04, 0x40
23591398Stmm};
23691398Stmm
23791398Stmmstatic isc_result_t
23891398Stmmopensslgost_todns(const dst_key_t *key, isc_buffer_t *data) {
23991398Stmm	EVP_PKEY *pkey;
24091398Stmm	isc_region_t r;
24191398Stmm	unsigned char der[37 + 64], *p;
24291398Stmm	int len;
24391398Stmm
24491398Stmm	REQUIRE(key->keydata.pkey != NULL);
24591398Stmm
24691398Stmm	pkey = key->keydata.pkey;
247108832Stmm
24891398Stmm	isc_buffer_availableregion(data, &r);
249174987Smarius	if (r.length < 64)
250174987Smarius		return (ISC_R_NOSPACE);
251174987Smarius
252108832Stmm	p = der;
25391398Stmm	len = i2d_PUBKEY(pkey, &p);
25491398Stmm	INSIST(len == sizeof(der));
25591398Stmm	INSIST(memcmp(gost_prefix, der, 37) == 0);
25691398Stmm	memmove(r.base, der + 37, 64);
25791398Stmm	isc_buffer_add(data, 64);
25891398Stmm
25991398Stmm	return (ISC_R_SUCCESS);
26091398Stmm}
261108832Stmm
26291398Stmmstatic isc_result_t
263174987Smariusopensslgost_fromdns(dst_key_t *key, isc_buffer_t *data) {
264174987Smarius	isc_region_t r;
265174987Smarius	EVP_PKEY *pkey = NULL;
266108832Stmm	unsigned char der[37 + 64];
26791398Stmm	const unsigned char *p;
26891398Stmm
26991398Stmm	isc_buffer_remainingregion(data, &r);
27091398Stmm	if (r.length == 0)
271172334Smarius		return (ISC_R_SUCCESS);
272172334Smarius
273177560Smarius	if (r.length != 64)
274172334Smarius		return (DST_R_INVALIDPUBLICKEY);
275172334Smarius	memmove(der, gost_prefix, 37);
27691398Stmm	memmove(der + 37, r.base, 64);
27791398Stmm	isc_buffer_forward(data, 64);
278172334Smarius
279172334Smarius	p = der;
280172334Smarius	if (d2i_PUBKEY(&pkey, &p, (long) sizeof(der)) == NULL)
281172334Smarius		return (dst__openssl_toresult2("d2i_PUBKEY",
282177560Smarius					       DST_R_OPENSSLFAILURE));
283172334Smarius	key->keydata.pkey = pkey;
284172334Smarius
285177560Smarius	return (ISC_R_SUCCESS);
286172334Smarius}
287172334Smarius
288172334Smariusstatic isc_result_t
289172334Smariusopensslgost_tofile(const dst_key_t *key, const char *directory) {
290172334Smarius	EVP_PKEY *pkey;
291172334Smarius	dst_private_t priv;
292172334Smarius	isc_result_t result;
293172334Smarius	unsigned char *der, *p;
294172334Smarius	int len;
295172334Smarius
296172334Smarius	if (key->keydata.pkey == NULL)
297172334Smarius		return (DST_R_NULLKEY);
298172334Smarius
299172334Smarius	if (key->external) {
300172334Smarius		priv.nelements = 0;
301172334Smarius		return (dst__privstruct_writefile(key, &priv, directory));
302172334Smarius	}
303177560Smarius
304172334Smarius	pkey = key->keydata.pkey;
305172334Smarius
306172334Smarius	len = i2d_PrivateKey(pkey, NULL);
307172334Smarius	der = isc_mem_get(key->mctx, (size_t) len);
308172334Smarius	if (der == NULL)
309172334Smarius		return (ISC_R_NOMEMORY);
310172334Smarius
311172334Smarius	p = der;
312172334Smarius	if (i2d_PrivateKey(pkey, &p) != len) {
313172334Smarius		result = dst__openssl_toresult2("i2d_PrivateKey",
314172334Smarius						DST_R_OPENSSLFAILURE);
315172334Smarius		goto fail;
316172334Smarius	}
317172334Smarius
318172334Smarius	priv.elements[0].tag = TAG_GOST_PRIVASN1;
319172334Smarius	priv.elements[0].length = len;
320172334Smarius	priv.elements[0].data = der;
321172334Smarius	priv.nelements = GOST_NTAGS;
322172334Smarius
323172334Smarius	result = dst__privstruct_writefile(key, &priv, directory);
324177560Smarius fail:
325172334Smarius	if (der != NULL)
326177560Smarius		isc_mem_put(key->mctx, der, (size_t) len);
327172334Smarius	return (result);
328177560Smarius}
329172334Smarius
330172334Smariusstatic isc_result_t
331172334Smariusopensslgost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
332172334Smarius	dst_private_t priv;
333172334Smarius	isc_result_t ret;
334172334Smarius	isc_mem_t *mctx = key->mctx;
335172334Smarius	EVP_PKEY *pkey = NULL;
336172334Smarius	const unsigned char *p;
337108832Stmm
33891398Stmm	UNUSED(pub);
33991398Stmm
34091398Stmm	/* read private key file */
34191398Stmm	ret = dst__privstruct_parse(key, DST_ALG_ECCGOST, lexer, mctx, &priv);
34291398Stmm	if (ret != ISC_R_SUCCESS)
34391398Stmm		return (ret);
34491398Stmm
34591398Stmm	if (key->external) {
34691398Stmm		INSIST(priv.nelements == 0);
347176996Smarius		if (pub == NULL)
34899726Sbenno			DST_RET(DST_R_INVALIDPRIVATEKEY);
349177560Smarius		key->keydata.pkey = pub->keydata.pkey;
35099726Sbenno		pub->keydata.pkey = NULL;
351176996Smarius	} else {
352177560Smarius		INSIST(priv.elements[0].tag == TAG_GOST_PRIVASN1);
353128588Stmm		p = priv.elements[0].data;
354128588Stmm		if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
35599726Sbenno				   (long) priv.elements[0].length) == NULL)
35691398Stmm			DST_RET(dst__openssl_toresult2("d2i_PrivateKey",
357147256Sbrooks						     DST_R_INVALIDPRIVATEKEY));
35891398Stmm		key->keydata.pkey = pkey;
35991398Stmm	}
360170273Syongari	key->key_size = EVP_PKEY_bits(pkey);
361149552Smarius	dst__privstruct_free(&priv, mctx);
362149552Smarius	memset(&priv, 0, sizeof(priv));
363170273Syongari	return (ISC_R_SUCCESS);
364170273Syongari
365170273Syongari err:
366149552Smarius	if (pkey != NULL)
36791398Stmm		EVP_PKEY_free(pkey);
36891398Stmm	opensslgost_destroy(key);
36991398Stmm	dst__privstruct_free(&priv, mctx);
37091398Stmm	memset(&priv, 0, sizeof(priv));
37191398Stmm	return (ret);
37291398Stmm}
373174987Smarius
374174987Smariusstatic void
37591398Stmmopensslgost_cleanup(void) {
376108832Stmm	if (e != NULL) {
37791398Stmm		ENGINE_finish(e);
378174987Smarius		ENGINE_free(e);
379174987Smarius		e = NULL;
38091398Stmm	}
381108832Stmm}
38291398Stmm
383108832Stmmstatic dst_func_t opensslgost_functions = {
384174987Smarius	opensslgost_createctx,
38591398Stmm	opensslgost_destroyctx,
38691398Stmm	opensslgost_adddata,
387174987Smarius	opensslgost_sign,
38891398Stmm	opensslgost_verify,
389174987Smarius	NULL, /*%< verify2 */
390108832Stmm	NULL, /*%< computesecret */
391174987Smarius	opensslgost_compare,
392108832Stmm	NULL, /*%< paramcompare */
393174987Smarius	opensslgost_generate,
39491398Stmm	opensslgost_isprivate,
395174987Smarius	opensslgost_destroy,
396147256Sbrooks	opensslgost_todns,
39791398Stmm	opensslgost_fromdns,
39891398Stmm	opensslgost_tofile,
39991398Stmm	opensslgost_parse,
400108964Stmm	opensslgost_cleanup,
401174987Smarius	NULL, /*%< fromlabel */
402108964Stmm	NULL, /*%< dump */
403147256Sbrooks	NULL  /*%< restore */
404108964Stmm};
405108964Stmm
406194886Smariusisc_result_t
407148369Smariusdst__opensslgost_init(dst_func_t **funcp) {
408147317Sbrooks	isc_result_t ret;
409148369Smarius
410150285Smarius	REQUIRE(funcp != NULL);
411150285Smarius
412150285Smarius	/* check if the gost engine works properly */
413150285Smarius	e = ENGINE_by_id("gost");
414147256Sbrooks	if (e == NULL)
415108964Stmm		return (dst__openssl_toresult2("ENGINE_by_id",
416108964Stmm					       DST_R_OPENSSLFAILURE));
417174987Smarius	if (ENGINE_init(e) <= 0) {
418108964Stmm		ENGINE_free(e);
419108964Stmm		e = NULL;
420108964Stmm		return (dst__openssl_toresult2("ENGINE_init",
421174987Smarius					       DST_R_OPENSSLFAILURE));
422108964Stmm	}
423108964Stmm	/* better than to rely on digest_gost symbol */
424108964Stmm	opensslgost_digest = ENGINE_get_digest(e, NID_id_GostR3411_94);
425179925Smarius	if (opensslgost_digest == NULL)
426108964Stmm		DST_RET(dst__openssl_toresult2("ENGINE_get_digest",
427108964Stmm					       DST_R_OPENSSLFAILURE));
428108964Stmm	/* from openssl.cnf */
429108964Stmm	if (ENGINE_register_pkey_asn1_meths(e) <= 0)
430108964Stmm		DST_RET(dst__openssl_toresult2(
431108964Stmm				"ENGINE_register_pkey_asn1_meths",
432108964Stmm				DST_R_OPENSSLFAILURE));
433108964Stmm	if (ENGINE_ctrl_cmd_string(e,
434108964Stmm				   "CRYPT_PARAMS",
435108964Stmm				   "id-Gost28147-89-CryptoPro-A-ParamSet",
436174987Smarius				   0) <= 0)
437108964Stmm		DST_RET(dst__openssl_toresult2("ENGINE_ctrl_cmd_string",
438147256Sbrooks					       DST_R_OPENSSLFAILURE));
439108964Stmm
440148369Smarius	if (*funcp == NULL)
441108964Stmm		*funcp = &opensslgost_functions;
442148369Smarius	return (ISC_R_SUCCESS);
443108964Stmm
444108964Stmm err:
445108964Stmm	ENGINE_finish(e);
446174987Smarius	ENGINE_free(e);
447108964Stmm	e = NULL;
448147256Sbrooks	return (ret);
449108964Stmm}
450148369Smarius
451149552Smarius#else /* HAVE_OPENSSL_GOST */
452149552Smarius
453149552Smarius#include <isc/util.h>
454149552Smarius
455172334SmariusEMPTY_TRANSLATION_UNIT
456108964Stmm
457148369Smarius#endif /* HAVE_OPENSSL_GOST */
458148369Smarius/*! \file */
459108964Stmm