1/*	$NetBSD: opensslecdsa_link.c,v 1.2.2.2 2012/12/15 05:39:58 riz Exp $	*/
2
3/*
4 * Copyright (C) 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 */
20
21#include <config.h>
22
23#ifdef HAVE_OPENSSL_ECDSA
24
25#if !defined(HAVE_EVP_SHA256) || !defined(HAVE_EVP_SHA384)
26#error "ECDSA without EVP for SHA2?"
27#endif
28
29#include <isc/entropy.h>
30#include <isc/mem.h>
31#include <isc/sha2.h>
32#include <isc/string.h>
33#include <isc/util.h>
34
35#include <dns/keyvalues.h>
36#include <dst/result.h>
37
38#include "dst_internal.h"
39#include "dst_openssl.h"
40#include "dst_parse.h"
41
42#include <openssl/err.h>
43#include <openssl/objects.h>
44#include <openssl/ecdsa.h>
45#include <openssl/bn.h>
46
47#ifndef NID_X9_62_prime256v1
48#error "P-256 group is not known (NID_X9_62_prime256v1)"
49#endif
50#ifndef NID_secp384r1
51#error "P-384 group is not known (NID_secp384r1)"
52#endif
53
54#define DST_RET(a) {ret = a; goto err;}
55
56static isc_result_t opensslecdsa_todns(const dst_key_t *key,
57				       isc_buffer_t *data);
58
59static isc_result_t
60opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) {
61	EVP_MD_CTX *evp_md_ctx;
62	const EVP_MD *type = NULL;
63
64	UNUSED(key);
65	REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
66		dctx->key->key_alg == DST_ALG_ECDSA384);
67
68	evp_md_ctx = EVP_MD_CTX_create();
69	if (evp_md_ctx == NULL)
70		return (ISC_R_NOMEMORY);
71	if (dctx->key->key_alg == DST_ALG_ECDSA256)
72		type = EVP_sha256();
73	else
74		type = EVP_sha384();
75
76	if (!EVP_DigestInit_ex(evp_md_ctx, type, NULL)) {
77		EVP_MD_CTX_destroy(evp_md_ctx);
78		return (dst__openssl_toresult2("EVP_DigestInit_ex",
79					       ISC_R_FAILURE));
80	}
81
82	dctx->ctxdata.evp_md_ctx = evp_md_ctx;
83
84	return (ISC_R_SUCCESS);
85}
86
87static void
88opensslecdsa_destroyctx(dst_context_t *dctx) {
89	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
90
91	REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
92		dctx->key->key_alg == DST_ALG_ECDSA384);
93
94	if (evp_md_ctx != NULL) {
95		EVP_MD_CTX_destroy(evp_md_ctx);
96		dctx->ctxdata.evp_md_ctx = NULL;
97	}
98}
99
100static isc_result_t
101opensslecdsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
102	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
103
104	REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
105		dctx->key->key_alg == DST_ALG_ECDSA384);
106
107	if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length))
108		return (dst__openssl_toresult2("EVP_DigestUpdate",
109					       ISC_R_FAILURE));
110
111	return (ISC_R_SUCCESS);
112}
113
114static int
115BN_bn2bin_fixed(BIGNUM *bn, unsigned char *buf, int size) {
116	int bytes = size - BN_num_bytes(bn);
117
118	while (bytes-- > 0)
119		*buf++ = 0;
120	BN_bn2bin(bn, buf);
121	return (size);
122}
123
124static isc_result_t
125opensslecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
126	isc_result_t ret;
127	dst_key_t *key = dctx->key;
128	isc_region_t r;
129	ECDSA_SIG *ecdsasig;
130	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
131	EVP_PKEY *pkey = key->keydata.pkey;
132	EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(pkey);
133	unsigned int dgstlen, siglen;
134	unsigned char digest[EVP_MAX_MD_SIZE];
135
136	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
137		key->key_alg == DST_ALG_ECDSA384);
138
139	if (eckey == NULL)
140		return (ISC_R_FAILURE);
141
142	if (key->key_alg == DST_ALG_ECDSA256)
143		siglen = DNS_SIG_ECDSA256SIZE;
144	else
145		siglen = DNS_SIG_ECDSA384SIZE;
146
147	isc_buffer_availableregion(sig, &r);
148	if (r.length < siglen)
149		DST_RET(ISC_R_NOSPACE);
150
151	if (!EVP_DigestFinal(evp_md_ctx, digest, &dgstlen))
152		DST_RET(dst__openssl_toresult2("EVP_DigestFinal",
153					       ISC_R_FAILURE));
154
155	ecdsasig = ECDSA_do_sign(digest, dgstlen, eckey);
156	if (ecdsasig == NULL)
157		DST_RET(dst__openssl_toresult2("ECDSA_do_sign",
158					       DST_R_SIGNFAILURE));
159	BN_bn2bin_fixed(ecdsasig->r, r.base, siglen / 2);
160	r.base += siglen / 2;
161	BN_bn2bin_fixed(ecdsasig->s, r.base, siglen / 2);
162	r.base += siglen / 2;
163	ECDSA_SIG_free(ecdsasig);
164	isc_buffer_add(sig, siglen);
165	ret = ISC_R_SUCCESS;
166
167 err:
168	if (eckey != NULL)
169		EC_KEY_free(eckey);
170	return (ret);
171}
172
173static isc_result_t
174opensslecdsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
175	isc_result_t ret;
176	dst_key_t *key = dctx->key;
177	int status;
178	unsigned char *cp = sig->base;
179	ECDSA_SIG *ecdsasig = NULL;
180	EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
181	EVP_PKEY *pkey = key->keydata.pkey;
182	EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(pkey);
183	unsigned int dgstlen, siglen;
184	unsigned char digest[EVP_MAX_MD_SIZE];
185
186	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
187		key->key_alg == DST_ALG_ECDSA384);
188
189	if (eckey == NULL)
190		return (ISC_R_FAILURE);
191
192	if (key->key_alg == DST_ALG_ECDSA256)
193		siglen = DNS_SIG_ECDSA256SIZE;
194	else
195		siglen = DNS_SIG_ECDSA384SIZE;
196
197	if (sig->length != siglen)
198		return (DST_R_VERIFYFAILURE);
199
200	if (!EVP_DigestFinal_ex(evp_md_ctx, digest, &dgstlen))
201		DST_RET (dst__openssl_toresult2("EVP_DigestFinal_ex",
202						ISC_R_FAILURE));
203
204	ecdsasig = ECDSA_SIG_new();
205	if (ecdsasig == NULL)
206		DST_RET (ISC_R_NOMEMORY);
207	ecdsasig->r = BN_bin2bn(cp, siglen / 2, NULL);
208	cp += siglen / 2;
209	ecdsasig->s = BN_bin2bn(cp, siglen / 2, NULL);
210	/* cp += siglen / 2; */
211
212	status = ECDSA_do_verify(digest, dgstlen, ecdsasig, eckey);
213	switch (status) {
214	case 1:
215		ret = ISC_R_SUCCESS;
216		break;
217	case 0:
218		ret = dst__openssl_toresult(DST_R_VERIFYFAILURE);
219		break;
220	default:
221		ret = dst__openssl_toresult2("ECDSA_do_verify",
222					     DST_R_VERIFYFAILURE);
223		break;
224	}
225
226 err:
227	if (ecdsasig != NULL)
228		ECDSA_SIG_free(ecdsasig);
229	if (eckey != NULL)
230		EC_KEY_free(eckey);
231	return (ret);
232}
233
234static isc_boolean_t
235opensslecdsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
236	isc_boolean_t ret;
237	int status;
238	EVP_PKEY *pkey1 = key1->keydata.pkey;
239	EVP_PKEY *pkey2 = key2->keydata.pkey;
240	EC_KEY *eckey1 = NULL;
241	EC_KEY *eckey2 = NULL;
242	const BIGNUM *priv1, *priv2;
243
244	if (pkey1 == NULL && pkey2 == NULL)
245		return (ISC_TRUE);
246	else if (pkey1 == NULL || pkey2 == NULL)
247		return (ISC_FALSE);
248
249	eckey1 = EVP_PKEY_get1_EC_KEY(pkey1);
250	eckey2 = EVP_PKEY_get1_EC_KEY(pkey2);
251	if (eckey1 == NULL && eckey2 == NULL) {
252		DST_RET (ISC_TRUE);
253	} else if (eckey1 == NULL || eckey2 == NULL)
254		DST_RET (ISC_FALSE);
255
256	status = EVP_PKEY_cmp(pkey1, pkey2);
257	if (status != 1)
258		DST_RET (ISC_FALSE);
259
260	priv1 = EC_KEY_get0_private_key(eckey1);
261	priv2 = EC_KEY_get0_private_key(eckey2);
262	if (priv1 != NULL || priv2 != NULL) {
263		if (priv1 == NULL || priv2 == NULL)
264			DST_RET (ISC_FALSE);
265		if (BN_cmp(priv1, priv2) != 0)
266			DST_RET (ISC_FALSE);
267	}
268	ret = ISC_TRUE;
269
270 err:
271	if (eckey1 != NULL)
272		EC_KEY_free(eckey1);
273	if (eckey2 != NULL)
274		EC_KEY_free(eckey2);
275	return (ret);
276}
277
278static isc_result_t
279opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
280	isc_result_t ret;
281	EVP_PKEY *pkey;
282	EC_KEY *eckey = NULL;
283	int group_nid;
284
285	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
286		key->key_alg == DST_ALG_ECDSA384);
287	UNUSED(unused);
288	UNUSED(callback);
289
290	if (key->key_alg == DST_ALG_ECDSA256)
291		group_nid = NID_X9_62_prime256v1;
292	else
293		group_nid = NID_secp384r1;
294
295	eckey = EC_KEY_new_by_curve_name(group_nid);
296	if (eckey == NULL)
297		return (dst__openssl_toresult2("EC_KEY_new_by_curve_name",
298					       DST_R_OPENSSLFAILURE));
299
300	if (EC_KEY_generate_key(eckey) != 1)
301		DST_RET (dst__openssl_toresult2("EC_KEY_generate_key",
302						DST_R_OPENSSLFAILURE));
303
304	pkey = EVP_PKEY_new();
305	if (pkey == NULL)
306		DST_RET (ISC_R_NOMEMORY);
307	if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) {
308		EVP_PKEY_free(pkey);
309		DST_RET (ISC_R_FAILURE);
310	}
311	key->keydata.pkey = pkey;
312	ret = ISC_R_SUCCESS;
313
314 err:
315	if (eckey != NULL)
316		EC_KEY_free(eckey);
317	return (ret);
318}
319
320static isc_boolean_t
321opensslecdsa_isprivate(const dst_key_t *key) {
322	isc_boolean_t ret;
323	EVP_PKEY *pkey = key->keydata.pkey;
324	EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(pkey);
325
326	ret = ISC_TF(eckey != NULL && EC_KEY_get0_private_key(eckey) != NULL);
327	if (eckey != NULL)
328		EC_KEY_free(eckey);
329	return (ret);
330}
331
332static void
333opensslecdsa_destroy(dst_key_t *key) {
334	EVP_PKEY *pkey = key->keydata.pkey;
335
336	EVP_PKEY_free(pkey);
337	key->keydata.pkey = NULL;
338}
339
340static isc_result_t
341opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) {
342	isc_result_t ret;
343	EVP_PKEY *pkey;
344	EC_KEY *eckey = NULL;
345	isc_region_t r;
346	int len;
347	unsigned char *cp;
348	unsigned char buf[DNS_KEY_ECDSA384SIZE + 1];
349
350	REQUIRE(key->keydata.pkey != NULL);
351
352	pkey = key->keydata.pkey;
353	eckey = EVP_PKEY_get1_EC_KEY(pkey);
354	if (eckey == NULL)
355		return (dst__openssl_toresult(ISC_R_FAILURE));
356	len = i2o_ECPublicKey(eckey, NULL);
357	/* skip form */
358	len--;
359
360	isc_buffer_availableregion(data, &r);
361	if (r.length < (unsigned int) len)
362		DST_RET (ISC_R_NOSPACE);
363	cp = buf;
364	if (!i2o_ECPublicKey(eckey, &cp))
365		DST_RET (dst__openssl_toresult(ISC_R_FAILURE));
366	memcpy(r.base, buf + 1, len);
367	isc_buffer_add(data, len);
368	ret = ISC_R_SUCCESS;
369
370 err:
371	if (eckey != NULL)
372		EC_KEY_free(eckey);
373	return (ret);
374}
375
376static isc_result_t
377opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
378	isc_result_t ret;
379	EVP_PKEY *pkey;
380	EC_KEY *eckey = NULL;
381	isc_region_t r;
382	int group_nid;
383	unsigned int len;
384	const unsigned char *cp;
385	unsigned char buf[DNS_KEY_ECDSA384SIZE + 1];
386
387	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
388		key->key_alg == DST_ALG_ECDSA384);
389
390	if (key->key_alg == DST_ALG_ECDSA256) {
391		len = DNS_KEY_ECDSA256SIZE;
392		group_nid = NID_X9_62_prime256v1;
393	} else {
394		len = DNS_KEY_ECDSA384SIZE;
395		group_nid = NID_secp384r1;
396	}
397
398	isc_buffer_remainingregion(data, &r);
399	if (r.length == 0)
400		return (ISC_R_SUCCESS);
401	if (r.length < len)
402		return (DST_R_INVALIDPUBLICKEY);
403
404	eckey = EC_KEY_new_by_curve_name(group_nid);
405	if (eckey == NULL)
406		return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
407
408	buf[0] = POINT_CONVERSION_UNCOMPRESSED;
409	memcpy(buf + 1, r.base, len);
410	cp = buf;
411	if (o2i_ECPublicKey(&eckey,
412			    (const unsigned char **) &cp,
413			    (long) len + 1) == NULL)
414		DST_RET (dst__openssl_toresult(DST_R_INVALIDPUBLICKEY));
415	if (EC_KEY_check_key(eckey) != 1)
416		DST_RET (dst__openssl_toresult(DST_R_INVALIDPUBLICKEY));
417
418	pkey = EVP_PKEY_new();
419	if (pkey == NULL)
420		DST_RET (ISC_R_NOMEMORY);
421	if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) {
422		EVP_PKEY_free(pkey);
423		DST_RET (dst__openssl_toresult(ISC_R_FAILURE));
424	}
425
426	isc_buffer_forward(data, len);
427	key->keydata.pkey = pkey;
428	ret = ISC_R_SUCCESS;
429
430 err:
431	if (eckey != NULL)
432		EC_KEY_free(eckey);
433	return (ret);
434}
435
436static isc_result_t
437opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
438	isc_result_t ret;
439	EVP_PKEY *pkey;
440	EC_KEY *eckey = NULL;
441	const BIGNUM *privkey;
442	dst_private_t priv;
443	unsigned char *buf = NULL;
444
445	if (key->keydata.pkey == NULL)
446		return (DST_R_NULLKEY);
447
448	pkey = key->keydata.pkey;
449	eckey = EVP_PKEY_get1_EC_KEY(pkey);
450	if (eckey == NULL)
451		return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
452	privkey = EC_KEY_get0_private_key(eckey);
453	if (privkey == NULL)
454		DST_RET (ISC_R_FAILURE);
455
456	buf = isc_mem_get(key->mctx, BN_num_bytes(privkey));
457	if (buf == NULL)
458		DST_RET (ISC_R_NOMEMORY);
459
460	priv.elements[0].tag = TAG_ECDSA_PRIVATEKEY;
461	priv.elements[0].length = BN_num_bytes(privkey);
462	BN_bn2bin(privkey, buf);
463	priv.elements[0].data = buf;
464	priv.nelements = ECDSA_NTAGS;
465	ret = dst__privstruct_writefile(key, &priv, directory);
466
467 err:
468	if (eckey != NULL)
469		EC_KEY_free(eckey);
470	if (buf != NULL)
471		isc_mem_put(key->mctx, buf, BN_num_bytes(privkey));
472	return (ret);
473}
474
475static isc_result_t
476ecdsa_check(EC_KEY *eckey, dst_key_t *pub)
477{
478	isc_result_t ret = ISC_R_FAILURE;
479	EVP_PKEY *pkey;
480	EC_KEY *pubeckey = NULL;
481	const EC_POINT *pubkey;
482
483	if (pub == NULL)
484		return (ISC_R_SUCCESS);
485	pkey = pub->keydata.pkey;
486	if (pkey == NULL)
487		return (ISC_R_SUCCESS);
488	pubeckey = EVP_PKEY_get1_EC_KEY(pkey);
489	if (pubeckey == NULL)
490		return (ISC_R_SUCCESS);
491	pubkey = EC_KEY_get0_public_key(pubeckey);
492	if (pubkey == NULL)
493		DST_RET (ISC_R_SUCCESS);
494	if (EC_KEY_set_public_key(eckey, pubkey) != 1)
495		DST_RET (ISC_R_SUCCESS);
496	if (EC_KEY_check_key(eckey) == 1)
497		DST_RET (ISC_R_SUCCESS);
498
499 err:
500	if (pubeckey != NULL)
501		EC_KEY_free(pubeckey);
502	return (ret);
503}
504
505static isc_result_t
506opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
507	dst_private_t priv;
508	isc_result_t ret;
509	EVP_PKEY *pkey;
510	EC_KEY *eckey = NULL;
511	BIGNUM *privkey;
512	int group_nid;
513	isc_mem_t *mctx = key->mctx;
514
515	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
516		key->key_alg == DST_ALG_ECDSA384);
517
518	if (key->key_alg == DST_ALG_ECDSA256)
519		group_nid = NID_X9_62_prime256v1;
520	else
521		group_nid = NID_secp384r1;
522
523	eckey = EC_KEY_new_by_curve_name(group_nid);
524	if (eckey == NULL)
525		return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
526
527	/* read private key file */
528	ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, mctx, &priv);
529	if (ret != ISC_R_SUCCESS)
530		goto err;
531
532	privkey = BN_bin2bn(priv.elements[0].data,
533			    priv.elements[0].length, NULL);
534	if (privkey == NULL)
535		DST_RET(ISC_R_NOMEMORY);
536	if (!EC_KEY_set_private_key(eckey, privkey))
537		DST_RET(ISC_R_NOMEMORY);
538	if (ecdsa_check(eckey, pub) != ISC_R_SUCCESS)
539		DST_RET(DST_R_INVALIDPRIVATEKEY);
540	dst__privstruct_free(&priv, mctx);
541	memset(&priv, 0, sizeof(priv));
542
543	pkey = EVP_PKEY_new();
544	if (pkey == NULL)
545		DST_RET (ISC_R_NOMEMORY);
546	if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) {
547		EVP_PKEY_free(pkey);
548		DST_RET (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
549	}
550	key->keydata.pkey = pkey;
551	ret = ISC_R_SUCCESS;
552
553 err:
554	if (eckey != NULL)
555		EC_KEY_free(eckey);
556	dst__privstruct_free(&priv, mctx);
557	memset(&priv, 0, sizeof(priv));
558	return (ret);
559}
560
561static dst_func_t opensslecdsa_functions = {
562	opensslecdsa_createctx,
563	opensslecdsa_destroyctx,
564	opensslecdsa_adddata,
565	opensslecdsa_sign,
566	opensslecdsa_verify,
567	NULL, /*%< verify2 */
568	NULL, /*%< computesecret */
569	opensslecdsa_compare,
570	NULL, /*%< paramcompare */
571	opensslecdsa_generate,
572	opensslecdsa_isprivate,
573	opensslecdsa_destroy,
574	opensslecdsa_todns,
575	opensslecdsa_fromdns,
576	opensslecdsa_tofile,
577	opensslecdsa_parse,
578	NULL, /*%< cleanup */
579	NULL, /*%< fromlabel */
580	NULL, /*%< dump */
581	NULL, /*%< restore */
582};
583
584isc_result_t
585dst__opensslecdsa_init(dst_func_t **funcp) {
586	REQUIRE(funcp != NULL);
587	if (*funcp == NULL)
588		*funcp = &opensslecdsa_functions;
589	return (ISC_R_SUCCESS);
590}
591
592#else /* HAVE_OPENSSL_ECDSA */
593
594#include <isc/util.h>
595
596EMPTY_TRANSLATION_UNIT
597
598#endif /* HAVE_OPENSSL_ECDSA */
599/*! \file */
600