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