1/*
2 * Portions Copyright (C) 2004-2009, 2011  Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 1999-2002  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
10 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
12 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
15 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
18 *
19 * Permission to use, copy, modify, and/or distribute this software for any
20 * purpose with or without fee is hereby granted, provided that the above
21 * copyright notice and this permission notice appear in all copies.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
24 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
26 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
29 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 */
31
32/*
33 * Principal Author: Brian Wellington
34 * $Id: openssldh_link.c,v 1.20 2011/01/11 23:47:13 tbox Exp $
35 */
36
37#ifdef OPENSSL
38
39#include <config.h>
40
41#include <ctype.h>
42
43#include <isc/mem.h>
44#include <isc/string.h>
45#include <isc/util.h>
46
47#include <dst/result.h>
48
49#include "dst_internal.h"
50#include "dst_openssl.h"
51#include "dst_parse.h"
52
53#define PRIME768 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088" \
54	"A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25" \
55	"F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"
56
57#define PRIME1024 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" \
58	"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF2" \
59	"5F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406" \
60	"B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"
61
62#define PRIME1536 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
63	"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
64	"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
65	"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
66	"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
67	"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
68	"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
69	"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
70
71
72static isc_result_t openssldh_todns(const dst_key_t *key, isc_buffer_t *data);
73
74static BIGNUM bn2, bn768, bn1024, bn1536;
75
76static isc_result_t
77openssldh_computesecret(const dst_key_t *pub, const dst_key_t *priv,
78			isc_buffer_t *secret)
79{
80	DH *dhpub, *dhpriv;
81	int ret;
82	isc_region_t r;
83	unsigned int len;
84
85	REQUIRE(pub->keydata.dh != NULL);
86	REQUIRE(priv->keydata.dh != NULL);
87
88	dhpub = pub->keydata.dh;
89	dhpriv = priv->keydata.dh;
90
91	len = DH_size(dhpriv);
92	isc_buffer_availableregion(secret, &r);
93	if (r.length < len)
94		return (ISC_R_NOSPACE);
95	ret = DH_compute_key(r.base, dhpub->pub_key, dhpriv);
96	if (ret == 0)
97		return (dst__openssl_toresult(DST_R_COMPUTESECRETFAILURE));
98	isc_buffer_add(secret, len);
99	return (ISC_R_SUCCESS);
100}
101
102static isc_boolean_t
103openssldh_compare(const dst_key_t *key1, const dst_key_t *key2) {
104	int status;
105	DH *dh1, *dh2;
106
107	dh1 = key1->keydata.dh;
108	dh2 = key2->keydata.dh;
109
110	if (dh1 == NULL && dh2 == NULL)
111		return (ISC_TRUE);
112	else if (dh1 == NULL || dh2 == NULL)
113		return (ISC_FALSE);
114
115	status = BN_cmp(dh1->p, dh2->p) ||
116		 BN_cmp(dh1->g, dh2->g) ||
117		 BN_cmp(dh1->pub_key, dh2->pub_key);
118
119	if (status != 0)
120		return (ISC_FALSE);
121
122	if (dh1->priv_key != NULL || dh2->priv_key != NULL) {
123		if (dh1->priv_key == NULL || dh2->priv_key == NULL)
124			return (ISC_FALSE);
125		if (BN_cmp(dh1->priv_key, dh2->priv_key) != 0)
126			return (ISC_FALSE);
127	}
128	return (ISC_TRUE);
129}
130
131static isc_boolean_t
132openssldh_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
133	int status;
134	DH *dh1, *dh2;
135
136	dh1 = key1->keydata.dh;
137	dh2 = key2->keydata.dh;
138
139	if (dh1 == NULL && dh2 == NULL)
140		return (ISC_TRUE);
141	else if (dh1 == NULL || dh2 == NULL)
142		return (ISC_FALSE);
143
144	status = BN_cmp(dh1->p, dh2->p) ||
145		 BN_cmp(dh1->g, dh2->g);
146
147	if (status != 0)
148		return (ISC_FALSE);
149	return (ISC_TRUE);
150}
151
152#if OPENSSL_VERSION_NUMBER > 0x00908000L
153static int
154progress_cb(int p, int n, BN_GENCB *cb)
155{
156	union {
157		void *dptr;
158		void (*fptr)(int);
159	} u;
160
161	UNUSED(n);
162
163	u.dptr = cb->arg;
164	if (u.fptr != NULL)
165		u.fptr(p);
166	return (1);
167}
168#endif
169
170static isc_result_t
171openssldh_generate(dst_key_t *key, int generator, void (*callback)(int)) {
172	DH *dh = NULL;
173#if OPENSSL_VERSION_NUMBER > 0x00908000L
174	BN_GENCB cb;
175	union {
176		void *dptr;
177		void (*fptr)(int);
178	} u;
179#else
180
181	UNUSED(callback);
182#endif
183
184	if (generator == 0) {
185		if (key->key_size == 768 ||
186		    key->key_size == 1024 ||
187		    key->key_size == 1536)
188		{
189			dh = DH_new();
190			if (dh == NULL)
191				return (dst__openssl_toresult(ISC_R_NOMEMORY));
192			if (key->key_size == 768)
193				dh->p = &bn768;
194			else if (key->key_size == 1024)
195				dh->p = &bn1024;
196			else
197				dh->p = &bn1536;
198			dh->g = &bn2;
199		} else
200			generator = 2;
201	}
202
203	if (generator != 0) {
204#if OPENSSL_VERSION_NUMBER > 0x00908000L
205		dh = DH_new();
206		if (dh == NULL)
207			return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
208
209		if (callback == NULL) {
210			BN_GENCB_set_old(&cb, NULL, NULL);
211		} else {
212			u.fptr = callback;
213			BN_GENCB_set(&cb, &progress_cb, u.dptr);
214		}
215
216		if (!DH_generate_parameters_ex(dh, key->key_size, generator,
217					       &cb)) {
218			DH_free(dh);
219			return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
220		}
221#else
222		dh = DH_generate_parameters(key->key_size, generator,
223					    NULL, NULL);
224#endif
225	}
226
227	if (dh == NULL)
228		return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
229
230	if (DH_generate_key(dh) == 0) {
231		DH_free(dh);
232		return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
233	}
234	dh->flags &= ~DH_FLAG_CACHE_MONT_P;
235
236	key->keydata.dh = dh;
237
238	return (ISC_R_SUCCESS);
239}
240
241static isc_boolean_t
242openssldh_isprivate(const dst_key_t *key) {
243	DH *dh = key->keydata.dh;
244	return (ISC_TF(dh != NULL && dh->priv_key != NULL));
245}
246
247static void
248openssldh_destroy(dst_key_t *key) {
249	DH *dh = key->keydata.dh;
250
251	if (dh == NULL)
252		return;
253
254	if (dh->p == &bn768 || dh->p == &bn1024 || dh->p == &bn1536)
255		dh->p = NULL;
256	if (dh->g == &bn2)
257		dh->g = NULL;
258	DH_free(dh);
259	key->keydata.dh = NULL;
260}
261
262static void
263uint16_toregion(isc_uint16_t val, isc_region_t *region) {
264	*region->base++ = (val & 0xff00) >> 8;
265	*region->base++ = (val & 0x00ff);
266}
267
268static isc_uint16_t
269uint16_fromregion(isc_region_t *region) {
270	isc_uint16_t val;
271	unsigned char *cp = region->base;
272
273	val = ((unsigned int)(cp[0])) << 8;
274	val |= ((unsigned int)(cp[1]));
275
276	region->base += 2;
277	return (val);
278}
279
280static isc_result_t
281openssldh_todns(const dst_key_t *key, isc_buffer_t *data) {
282	DH *dh;
283	isc_region_t r;
284	isc_uint16_t dnslen, plen, glen, publen;
285
286	REQUIRE(key->keydata.dh != NULL);
287
288	dh = key->keydata.dh;
289
290	isc_buffer_availableregion(data, &r);
291
292	if (dh->g == &bn2 &&
293	    (dh->p == &bn768 || dh->p == &bn1024 || dh->p == &bn1536)) {
294		plen = 1;
295		glen = 0;
296	}
297	else {
298		plen = BN_num_bytes(dh->p);
299		glen = BN_num_bytes(dh->g);
300	}
301	publen = BN_num_bytes(dh->pub_key);
302	dnslen = plen + glen + publen + 6;
303	if (r.length < (unsigned int) dnslen)
304		return (ISC_R_NOSPACE);
305
306	uint16_toregion(plen, &r);
307	if (plen == 1) {
308		if (dh->p == &bn768)
309			*r.base = 1;
310		else if (dh->p == &bn1024)
311			*r.base = 2;
312		else
313			*r.base = 3;
314	}
315	else
316		BN_bn2bin(dh->p, r.base);
317	r.base += plen;
318
319	uint16_toregion(glen, &r);
320	if (glen > 0)
321		BN_bn2bin(dh->g, r.base);
322	r.base += glen;
323
324	uint16_toregion(publen, &r);
325	BN_bn2bin(dh->pub_key, r.base);
326	r.base += publen;
327
328	isc_buffer_add(data, dnslen);
329
330	return (ISC_R_SUCCESS);
331}
332
333static isc_result_t
334openssldh_fromdns(dst_key_t *key, isc_buffer_t *data) {
335	DH *dh;
336	isc_region_t r;
337	isc_uint16_t plen, glen, publen;
338	int special = 0;
339
340	isc_buffer_remainingregion(data, &r);
341	if (r.length == 0)
342		return (ISC_R_SUCCESS);
343
344	dh = DH_new();
345	if (dh == NULL)
346		return (dst__openssl_toresult(ISC_R_NOMEMORY));
347	dh->flags &= ~DH_FLAG_CACHE_MONT_P;
348
349	/*
350	 * Read the prime length.  1 & 2 are table entries, > 16 means a
351	 * prime follows, otherwise an error.
352	 */
353	if (r.length < 2) {
354		DH_free(dh);
355		return (DST_R_INVALIDPUBLICKEY);
356	}
357	plen = uint16_fromregion(&r);
358	if (plen < 16 && plen != 1 && plen != 2) {
359		DH_free(dh);
360		return (DST_R_INVALIDPUBLICKEY);
361	}
362	if (r.length < plen) {
363		DH_free(dh);
364		return (DST_R_INVALIDPUBLICKEY);
365	}
366	if (plen == 1 || plen == 2) {
367		if (plen == 1)
368			special = *r.base++;
369		else
370			special = uint16_fromregion(&r);
371		switch (special) {
372			case 1:
373				dh->p = &bn768;
374				break;
375			case 2:
376				dh->p = &bn1024;
377				break;
378			case 3:
379				dh->p = &bn1536;
380				break;
381			default:
382				DH_free(dh);
383				return (DST_R_INVALIDPUBLICKEY);
384		}
385	}
386	else {
387		dh->p = BN_bin2bn(r.base, plen, NULL);
388		r.base += plen;
389	}
390
391	/*
392	 * Read the generator length.  This should be 0 if the prime was
393	 * special, but it might not be.  If it's 0 and the prime is not
394	 * special, we have a problem.
395	 */
396	if (r.length < 2) {
397		DH_free(dh);
398		return (DST_R_INVALIDPUBLICKEY);
399	}
400	glen = uint16_fromregion(&r);
401	if (r.length < glen) {
402		DH_free(dh);
403		return (DST_R_INVALIDPUBLICKEY);
404	}
405	if (special != 0) {
406		if (glen == 0)
407			dh->g = &bn2;
408		else {
409			dh->g = BN_bin2bn(r.base, glen, NULL);
410			if (BN_cmp(dh->g, &bn2) == 0) {
411				BN_free(dh->g);
412				dh->g = &bn2;
413			}
414			else {
415				DH_free(dh);
416				return (DST_R_INVALIDPUBLICKEY);
417			}
418		}
419	}
420	else {
421		if (glen == 0) {
422			DH_free(dh);
423			return (DST_R_INVALIDPUBLICKEY);
424		}
425		dh->g = BN_bin2bn(r.base, glen, NULL);
426	}
427	r.base += glen;
428
429	if (r.length < 2) {
430		DH_free(dh);
431		return (DST_R_INVALIDPUBLICKEY);
432	}
433	publen = uint16_fromregion(&r);
434	if (r.length < publen) {
435		DH_free(dh);
436		return (DST_R_INVALIDPUBLICKEY);
437	}
438	dh->pub_key = BN_bin2bn(r.base, publen, NULL);
439	r.base += publen;
440
441	key->key_size = BN_num_bits(dh->p);
442
443	isc_buffer_forward(data, plen + glen + publen + 6);
444
445	key->keydata.dh = dh;
446
447	return (ISC_R_SUCCESS);
448}
449
450static isc_result_t
451openssldh_tofile(const dst_key_t *key, const char *directory) {
452	int i;
453	DH *dh;
454	dst_private_t priv;
455	unsigned char *bufs[4];
456	isc_result_t result;
457
458	if (key->keydata.dh == NULL)
459		return (DST_R_NULLKEY);
460
461	dh = key->keydata.dh;
462
463	for (i = 0; i < 4; i++) {
464		bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(dh->p));
465		if (bufs[i] == NULL) {
466			result = ISC_R_NOMEMORY;
467			goto fail;
468		}
469	}
470
471	i = 0;
472
473	priv.elements[i].tag = TAG_DH_PRIME;
474	priv.elements[i].length = BN_num_bytes(dh->p);
475	BN_bn2bin(dh->p, bufs[i]);
476	priv.elements[i].data = bufs[i];
477	i++;
478
479	priv.elements[i].tag = TAG_DH_GENERATOR;
480	priv.elements[i].length = BN_num_bytes(dh->g);
481	BN_bn2bin(dh->g, bufs[i]);
482	priv.elements[i].data = bufs[i];
483	i++;
484
485	priv.elements[i].tag = TAG_DH_PRIVATE;
486	priv.elements[i].length = BN_num_bytes(dh->priv_key);
487	BN_bn2bin(dh->priv_key, bufs[i]);
488	priv.elements[i].data = bufs[i];
489	i++;
490
491	priv.elements[i].tag = TAG_DH_PUBLIC;
492	priv.elements[i].length = BN_num_bytes(dh->pub_key);
493	BN_bn2bin(dh->pub_key, bufs[i]);
494	priv.elements[i].data = bufs[i];
495	i++;
496
497	priv.nelements = i;
498	result = dst__privstruct_writefile(key, &priv, directory);
499 fail:
500	for (i = 0; i < 4; i++) {
501		if (bufs[i] == NULL)
502			break;
503		isc_mem_put(key->mctx, bufs[i], BN_num_bytes(dh->p));
504	}
505	return (result);
506}
507
508static isc_result_t
509openssldh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
510	dst_private_t priv;
511	isc_result_t ret;
512	int i;
513	DH *dh = NULL;
514	isc_mem_t *mctx;
515#define DST_RET(a) {ret = a; goto err;}
516
517	UNUSED(pub);
518	mctx = key->mctx;
519
520	/* read private key file */
521	ret = dst__privstruct_parse(key, DST_ALG_DH, lexer, mctx, &priv);
522	if (ret != ISC_R_SUCCESS)
523		return (ret);
524
525	dh = DH_new();
526	if (dh == NULL)
527		DST_RET(ISC_R_NOMEMORY);
528	dh->flags &= ~DH_FLAG_CACHE_MONT_P;
529	key->keydata.dh = dh;
530
531	for (i = 0; i < priv.nelements; i++) {
532		BIGNUM *bn;
533		bn = BN_bin2bn(priv.elements[i].data,
534			       priv.elements[i].length, NULL);
535		if (bn == NULL)
536			DST_RET(ISC_R_NOMEMORY);
537
538		switch (priv.elements[i].tag) {
539			case TAG_DH_PRIME:
540				dh->p = bn;
541				break;
542			case TAG_DH_GENERATOR:
543				dh->g = bn;
544				break;
545			case TAG_DH_PRIVATE:
546				dh->priv_key = bn;
547				break;
548			case TAG_DH_PUBLIC:
549				dh->pub_key = bn;
550				break;
551		}
552	}
553	dst__privstruct_free(&priv, mctx);
554
555	key->key_size = BN_num_bits(dh->p);
556
557	if ((key->key_size == 768 ||
558	     key->key_size == 1024 ||
559	     key->key_size == 1536) &&
560	    BN_cmp(dh->g, &bn2) == 0)
561	{
562		if (key->key_size == 768 && BN_cmp(dh->p, &bn768) == 0) {
563			BN_free(dh->p);
564			BN_free(dh->g);
565			dh->p = &bn768;
566			dh->g = &bn2;
567		} else if (key->key_size == 1024 &&
568			   BN_cmp(dh->p, &bn1024) == 0) {
569			BN_free(dh->p);
570			BN_free(dh->g);
571			dh->p = &bn1024;
572			dh->g = &bn2;
573		} else if (key->key_size == 1536 &&
574			   BN_cmp(dh->p, &bn1536) == 0) {
575			BN_free(dh->p);
576			BN_free(dh->g);
577			dh->p = &bn1536;
578			dh->g = &bn2;
579		}
580	}
581
582	return (ISC_R_SUCCESS);
583
584 err:
585	openssldh_destroy(key);
586	dst__privstruct_free(&priv, mctx);
587	memset(&priv, 0, sizeof(priv));
588	return (ret);
589}
590
591static void
592BN_fromhex(BIGNUM *b, const char *str) {
593	static const char hexdigits[] = "0123456789abcdef";
594	unsigned char data[512];
595	unsigned int i;
596	BIGNUM *out;
597
598	RUNTIME_CHECK(strlen(str) < 1024U && strlen(str) % 2 == 0U);
599	for (i = 0; i < strlen(str); i += 2) {
600		char *s;
601		unsigned int high, low;
602
603		s = strchr(hexdigits, tolower((unsigned char)str[i]));
604		RUNTIME_CHECK(s != NULL);
605		high = s - hexdigits;
606
607		s = strchr(hexdigits, tolower((unsigned char)str[i + 1]));
608		RUNTIME_CHECK(s != NULL);
609		low = s - hexdigits;
610
611		data[i/2] = (unsigned char)((high << 4) + low);
612	}
613	out = BN_bin2bn(data, strlen(str)/2, b);
614	RUNTIME_CHECK(out != NULL);
615}
616
617static void
618openssldh_cleanup(void) {
619	BN_free(&bn2);
620	BN_free(&bn768);
621	BN_free(&bn1024);
622	BN_free(&bn1536);
623}
624
625static dst_func_t openssldh_functions = {
626	NULL, /*%< createctx */
627	NULL, /*%< destroyctx */
628	NULL, /*%< adddata */
629	NULL, /*%< openssldh_sign */
630	NULL, /*%< openssldh_verify */
631	openssldh_computesecret,
632	openssldh_compare,
633	openssldh_paramcompare,
634	openssldh_generate,
635	openssldh_isprivate,
636	openssldh_destroy,
637	openssldh_todns,
638	openssldh_fromdns,
639	openssldh_tofile,
640	openssldh_parse,
641	openssldh_cleanup,
642	NULL, /*%< fromlabel */
643	NULL, /*%< dump */
644	NULL, /*%< restore */
645};
646
647isc_result_t
648dst__openssldh_init(dst_func_t **funcp) {
649	REQUIRE(funcp != NULL);
650	if (*funcp == NULL) {
651		BN_init(&bn2);
652		BN_init(&bn768);
653		BN_init(&bn1024);
654		BN_init(&bn1536);
655		BN_set_word(&bn2, 2);
656		BN_fromhex(&bn768, PRIME768);
657		BN_fromhex(&bn1024, PRIME1024);
658		BN_fromhex(&bn1536, PRIME1536);
659		*funcp = &openssldh_functions;
660	}
661	return (ISC_R_SUCCESS);
662}
663
664#else /* OPENSSL */
665
666#include <isc/util.h>
667
668EMPTY_TRANSLATION_UNIT
669
670#endif /* OPENSSL */
671/*! \file */
672