1/*
2 * Copyright (C) 2010-2014  Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id: opensslgost_link.c,v 1.5 2011/01/19 23:47:12 tbox Exp $ */
18
19#include <config.h>
20
21#ifdef HAVE_OPENSSL_GOST
22
23#include <isc/entropy.h>
24#include <isc/mem.h>
25#include <isc/string.h>
26#include <isc/util.h>
27
28#include <dst/result.h>
29
30#include "dst_internal.h"
31#include "dst_openssl.h"
32#include "dst_parse.h"
33
34#include <openssl/err.h>
35#include <openssl/objects.h>
36#include <openssl/rsa.h>
37#include <openssl/engine.h>
38
39static ENGINE *e = NULL;
40static const EVP_MD *opensslgost_digest;
41extern const EVP_MD *EVP_gost(void);
42
43const EVP_MD *EVP_gost(void) {
44	return (opensslgost_digest);
45}
46
47#define DST_RET(a) {ret = a; goto err;}
48
49static isc_result_t opensslgost_todns(const dst_key_t *key,
50				      isc_buffer_t *data);
51
52static isc_result_t
53opensslgost_createctx(dst_key_t *key, dst_context_t *dctx) {
54	EVP_MD_CTX *evp_md_ctx;
55	const EVP_MD *md = EVP_gost();
56
57	UNUSED(key);
58
59	if (md == NULL)
60		return (DST_R_OPENSSLFAILURE);
61
62	evp_md_ctx = EVP_MD_CTX_create();
63	if (evp_md_ctx == NULL)
64		return (ISC_R_NOMEMORY);
65
66	if (!EVP_DigestInit_ex(evp_md_ctx, md, NULL)) {
67		EVP_MD_CTX_destroy(evp_md_ctx);
68		return (ISC_R_FAILURE);
69	}
70	dctx->ctxdata.evp_md_ctx = evp_md_ctx;
71
72	return (ISC_R_SUCCESS);
73}
74
75static void
76opensslgost_destroyctx(dst_context_t *dctx) {
77	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
78
79	if (evp_md_ctx != NULL) {
80		EVP_MD_CTX_destroy(evp_md_ctx);
81		dctx->ctxdata.evp_md_ctx = NULL;
82	}
83}
84
85static isc_result_t
86opensslgost_adddata(dst_context_t *dctx, const isc_region_t *data) {
87	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
88
89	if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length))
90		return (ISC_R_FAILURE);
91
92	return (ISC_R_SUCCESS);
93}
94
95static isc_result_t
96opensslgost_sign(dst_context_t *dctx, isc_buffer_t *sig) {
97	dst_key_t *key = dctx->key;
98	isc_region_t r;
99	unsigned int siglen = 0;
100	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
101	EVP_PKEY *pkey = key->keydata.pkey;
102
103	isc_buffer_availableregion(sig, &r);
104
105	if (r.length < (unsigned int) EVP_PKEY_size(pkey))
106		return (ISC_R_NOSPACE);
107
108	if (!EVP_SignFinal(evp_md_ctx, r.base, &siglen, pkey))
109		return (ISC_R_FAILURE);
110
111	isc_buffer_add(sig, siglen);
112
113	return (ISC_R_SUCCESS);
114}
115
116static isc_result_t
117opensslgost_verify(dst_context_t *dctx, const isc_region_t *sig) {
118	dst_key_t *key = dctx->key;
119	int status = 0;
120	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
121	EVP_PKEY *pkey = key->keydata.pkey;
122
123	status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey);
124	switch (status) {
125	case 1:
126		return (ISC_R_SUCCESS);
127	case 0:
128		return (dst__openssl_toresult(DST_R_VERIFYFAILURE));
129	default:
130		return (dst__openssl_toresult3(dctx->category,
131					       "EVP_VerifyFinal",
132					       DST_R_VERIFYFAILURE));
133	}
134}
135
136static isc_boolean_t
137opensslgost_compare(const dst_key_t *key1, const dst_key_t *key2) {
138	EVP_PKEY *pkey1, *pkey2;
139
140	pkey1 = key1->keydata.pkey;
141	pkey2 = key2->keydata.pkey;
142
143	if (pkey1 == NULL && pkey2 == NULL)
144		return (ISC_TRUE);
145	else if (pkey1 == NULL || pkey2 == NULL)
146		return (ISC_FALSE);
147
148	if (EVP_PKEY_cmp(pkey1, pkey2) != 1)
149		return (ISC_FALSE);
150	return (ISC_TRUE);
151}
152
153static int
154progress_cb(EVP_PKEY_CTX *ctx)
155{
156	union {
157		void *dptr;
158		void (*fptr)(int);
159	} u;
160	int p;
161
162	u.dptr = EVP_PKEY_CTX_get_app_data(ctx);
163	p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
164	if (u.fptr != NULL)
165		u.fptr(p);
166	return (1);
167}
168
169static isc_result_t
170opensslgost_generate(dst_key_t *key, int unused, void (*callback)(int)) {
171	EVP_PKEY_CTX *ctx;
172	union {
173		void *dptr;
174		void (*fptr)(int);
175	} u;
176	EVP_PKEY *pkey = NULL;
177	isc_result_t ret;
178
179	UNUSED(unused);
180	ctx = EVP_PKEY_CTX_new_id(NID_id_GostR3410_2001, NULL);
181	if (ctx == NULL)
182		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_id",
183					       DST_R_OPENSSLFAILURE));
184	if (callback != NULL) {
185		u.fptr = callback;
186		EVP_PKEY_CTX_set_app_data(ctx, u.dptr);
187		EVP_PKEY_CTX_set_cb(ctx, &progress_cb);
188	}
189	if (EVP_PKEY_keygen_init(ctx) <= 0)
190		DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init",
191					       DST_R_OPENSSLFAILURE));
192	if (EVP_PKEY_CTX_ctrl_str(ctx, "paramset", "A") <= 0)
193		DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_ctrl_str",
194					       DST_R_OPENSSLFAILURE));
195	if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
196		DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen",
197					       DST_R_OPENSSLFAILURE));
198	key->keydata.pkey = pkey;
199	EVP_PKEY_CTX_free(ctx);
200	return (ISC_R_SUCCESS);
201
202err:
203	if (pkey != NULL)
204		EVP_PKEY_free(pkey);
205	if (ctx != NULL)
206		EVP_PKEY_CTX_free(ctx);
207	return (ret);
208}
209
210static isc_boolean_t
211opensslgost_isprivate(const dst_key_t *key) {
212	EVP_PKEY *pkey = key->keydata.pkey;
213	EC_KEY *ec;
214
215	INSIST(pkey != NULL);
216
217	ec = EVP_PKEY_get0(pkey);
218	return (ISC_TF(ec != NULL && EC_KEY_get0_private_key(ec) != NULL));
219}
220
221static void
222opensslgost_destroy(dst_key_t *key) {
223	EVP_PKEY *pkey = key->keydata.pkey;
224
225	EVP_PKEY_free(pkey);
226	key->keydata.pkey = NULL;
227}
228
229unsigned char gost_prefix[37] = {
230	0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85,
231	0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07,
232	0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01, 0x06,
233	0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01,
234	0x03, 0x43, 0x00, 0x04, 0x40
235};
236
237static isc_result_t
238opensslgost_todns(const dst_key_t *key, isc_buffer_t *data) {
239	EVP_PKEY *pkey;
240	isc_region_t r;
241	unsigned char der[37 + 64], *p;
242	int len;
243
244	REQUIRE(key->keydata.pkey != NULL);
245
246	pkey = key->keydata.pkey;
247
248	isc_buffer_availableregion(data, &r);
249	if (r.length < 64)
250		return (ISC_R_NOSPACE);
251
252	p = der;
253	len = i2d_PUBKEY(pkey, &p);
254	INSIST(len == sizeof(der));
255	INSIST(memcmp(gost_prefix, der, 37) == 0);
256	memmove(r.base, der + 37, 64);
257	isc_buffer_add(data, 64);
258
259	return (ISC_R_SUCCESS);
260}
261
262static isc_result_t
263opensslgost_fromdns(dst_key_t *key, isc_buffer_t *data) {
264	isc_region_t r;
265	EVP_PKEY *pkey = NULL;
266	unsigned char der[37 + 64];
267	const unsigned char *p;
268
269	isc_buffer_remainingregion(data, &r);
270	if (r.length == 0)
271		return (ISC_R_SUCCESS);
272
273	if (r.length != 64)
274		return (DST_R_INVALIDPUBLICKEY);
275	memmove(der, gost_prefix, 37);
276	memmove(der + 37, r.base, 64);
277	isc_buffer_forward(data, 64);
278
279	p = der;
280	if (d2i_PUBKEY(&pkey, &p, (long) sizeof(der)) == NULL)
281		return (dst__openssl_toresult2("d2i_PUBKEY",
282					       DST_R_OPENSSLFAILURE));
283	key->keydata.pkey = pkey;
284
285	return (ISC_R_SUCCESS);
286}
287
288static isc_result_t
289opensslgost_tofile(const dst_key_t *key, const char *directory) {
290	EVP_PKEY *pkey;
291	dst_private_t priv;
292	isc_result_t result;
293	unsigned char *der, *p;
294	int len;
295
296	if (key->keydata.pkey == NULL)
297		return (DST_R_NULLKEY);
298
299	if (key->external) {
300		priv.nelements = 0;
301		return (dst__privstruct_writefile(key, &priv, directory));
302	}
303
304	pkey = key->keydata.pkey;
305
306	len = i2d_PrivateKey(pkey, NULL);
307	der = isc_mem_get(key->mctx, (size_t) len);
308	if (der == NULL)
309		return (ISC_R_NOMEMORY);
310
311	p = der;
312	if (i2d_PrivateKey(pkey, &p) != len) {
313		result = dst__openssl_toresult2("i2d_PrivateKey",
314						DST_R_OPENSSLFAILURE);
315		goto fail;
316	}
317
318	priv.elements[0].tag = TAG_GOST_PRIVASN1;
319	priv.elements[0].length = len;
320	priv.elements[0].data = der;
321	priv.nelements = GOST_NTAGS;
322
323	result = dst__privstruct_writefile(key, &priv, directory);
324 fail:
325	if (der != NULL)
326		isc_mem_put(key->mctx, der, (size_t) len);
327	return (result);
328}
329
330static isc_result_t
331opensslgost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
332	dst_private_t priv;
333	isc_result_t ret;
334	isc_mem_t *mctx = key->mctx;
335	EVP_PKEY *pkey = NULL;
336	const unsigned char *p;
337
338	UNUSED(pub);
339
340	/* read private key file */
341	ret = dst__privstruct_parse(key, DST_ALG_ECCGOST, lexer, mctx, &priv);
342	if (ret != ISC_R_SUCCESS)
343		return (ret);
344
345	if (key->external) {
346		INSIST(priv.nelements == 0);
347		if (pub == NULL)
348			DST_RET(DST_R_INVALIDPRIVATEKEY);
349		key->keydata.pkey = pub->keydata.pkey;
350		pub->keydata.pkey = NULL;
351	} else {
352		INSIST(priv.elements[0].tag == TAG_GOST_PRIVASN1);
353		p = priv.elements[0].data;
354		if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
355				   (long) priv.elements[0].length) == NULL)
356			DST_RET(dst__openssl_toresult2("d2i_PrivateKey",
357						     DST_R_INVALIDPRIVATEKEY));
358		key->keydata.pkey = pkey;
359	}
360	key->key_size = EVP_PKEY_bits(pkey);
361	dst__privstruct_free(&priv, mctx);
362	memset(&priv, 0, sizeof(priv));
363	return (ISC_R_SUCCESS);
364
365 err:
366	if (pkey != NULL)
367		EVP_PKEY_free(pkey);
368	opensslgost_destroy(key);
369	dst__privstruct_free(&priv, mctx);
370	memset(&priv, 0, sizeof(priv));
371	return (ret);
372}
373
374static void
375opensslgost_cleanup(void) {
376	if (e != NULL) {
377		ENGINE_finish(e);
378		ENGINE_free(e);
379		e = NULL;
380	}
381}
382
383static dst_func_t opensslgost_functions = {
384	opensslgost_createctx,
385	opensslgost_destroyctx,
386	opensslgost_adddata,
387	opensslgost_sign,
388	opensslgost_verify,
389	NULL, /*%< verify2 */
390	NULL, /*%< computesecret */
391	opensslgost_compare,
392	NULL, /*%< paramcompare */
393	opensslgost_generate,
394	opensslgost_isprivate,
395	opensslgost_destroy,
396	opensslgost_todns,
397	opensslgost_fromdns,
398	opensslgost_tofile,
399	opensslgost_parse,
400	opensslgost_cleanup,
401	NULL, /*%< fromlabel */
402	NULL, /*%< dump */
403	NULL  /*%< restore */
404};
405
406isc_result_t
407dst__opensslgost_init(dst_func_t **funcp) {
408	isc_result_t ret;
409
410	REQUIRE(funcp != NULL);
411
412	/* check if the gost engine works properly */
413	e = ENGINE_by_id("gost");
414	if (e == NULL)
415		return (dst__openssl_toresult2("ENGINE_by_id",
416					       DST_R_OPENSSLFAILURE));
417	if (ENGINE_init(e) <= 0) {
418		ENGINE_free(e);
419		e = NULL;
420		return (dst__openssl_toresult2("ENGINE_init",
421					       DST_R_OPENSSLFAILURE));
422	}
423	/* better than to rely on digest_gost symbol */
424	opensslgost_digest = ENGINE_get_digest(e, NID_id_GostR3411_94);
425	if (opensslgost_digest == NULL)
426		DST_RET(dst__openssl_toresult2("ENGINE_get_digest",
427					       DST_R_OPENSSLFAILURE));
428	/* from openssl.cnf */
429	if (ENGINE_register_pkey_asn1_meths(e) <= 0)
430		DST_RET(dst__openssl_toresult2(
431				"ENGINE_register_pkey_asn1_meths",
432				DST_R_OPENSSLFAILURE));
433	if (ENGINE_ctrl_cmd_string(e,
434				   "CRYPT_PARAMS",
435				   "id-Gost28147-89-CryptoPro-A-ParamSet",
436				   0) <= 0)
437		DST_RET(dst__openssl_toresult2("ENGINE_ctrl_cmd_string",
438					       DST_R_OPENSSLFAILURE));
439
440	if (*funcp == NULL)
441		*funcp = &opensslgost_functions;
442	return (ISC_R_SUCCESS);
443
444 err:
445	ENGINE_finish(e);
446	ENGINE_free(e);
447	e = NULL;
448	return (ret);
449}
450
451#else /* HAVE_OPENSSL_GOST */
452
453#include <isc/util.h>
454
455EMPTY_TRANSLATION_UNIT
456
457#endif /* HAVE_OPENSSL_GOST */
458/*! \file */
459