1143731Sdougb/*
2262706Serwin * Portions Copyright (C) 2004-2009, 2011-2013  Internet Systems Consortium, Inc. ("ISC")
3143731Sdougb * Portions Copyright (C) 1999-2002  Internet Software Consortium.
4193149Sdougb *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6193149Sdougb * purpose with or without fee is hereby granted, provided that the above
7193149Sdougb * copyright notice and this permission notice appear in all copies.
8193149Sdougb *
9193149Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
10193149Sdougb * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11193149Sdougb * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
12193149Sdougb * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13193149Sdougb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14193149Sdougb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
15193149Sdougb * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16193149Sdougb *
17143731Sdougb * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
18143731Sdougb *
19174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
20143731Sdougb * purpose with or without fee is hereby granted, provided that the above
21143731Sdougb * copyright notice and this permission notice appear in all copies.
22143731Sdougb *
23143731Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
24143731Sdougb * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
25143731Sdougb * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
26143731Sdougb * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27143731Sdougb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28143731Sdougb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
29143731Sdougb * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30143731Sdougb */
31143731Sdougb
32143731Sdougb/*
33143731Sdougb * Principal Author: Brian Wellington
34234010Sdougb * $Id: openssldh_link.c,v 1.20 2011/01/11 23:47:13 tbox Exp $
35143731Sdougb */
36143731Sdougb
37143731Sdougb#ifdef OPENSSL
38143731Sdougb
39143731Sdougb#include <config.h>
40143731Sdougb
41143731Sdougb#include <ctype.h>
42143731Sdougb
43143731Sdougb#include <isc/mem.h>
44143731Sdougb#include <isc/string.h>
45143731Sdougb#include <isc/util.h>
46143731Sdougb
47143731Sdougb#include <dst/result.h>
48143731Sdougb
49143731Sdougb#include "dst_internal.h"
50143731Sdougb#include "dst_openssl.h"
51143731Sdougb#include "dst_parse.h"
52143731Sdougb
53143731Sdougb#define PRIME768 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088" \
54143731Sdougb	"A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25" \
55143731Sdougb	"F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"
56143731Sdougb
57143731Sdougb#define PRIME1024 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" \
58143731Sdougb	"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF2" \
59143731Sdougb	"5F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406" \
60143731Sdougb	"B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"
61143731Sdougb
62143731Sdougb#define PRIME1536 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
63143731Sdougb	"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
64143731Sdougb	"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
65143731Sdougb	"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
66143731Sdougb	"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
67143731Sdougb	"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
68143731Sdougb	"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
69143731Sdougb	"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
70143731Sdougb
71143731Sdougb
72143731Sdougbstatic isc_result_t openssldh_todns(const dst_key_t *key, isc_buffer_t *data);
73143731Sdougb
74143731Sdougbstatic BIGNUM bn2, bn768, bn1024, bn1536;
75143731Sdougb
76143731Sdougbstatic isc_result_t
77143731Sdougbopenssldh_computesecret(const dst_key_t *pub, const dst_key_t *priv,
78143731Sdougb			isc_buffer_t *secret)
79143731Sdougb{
80143731Sdougb	DH *dhpub, *dhpriv;
81143731Sdougb	int ret;
82143731Sdougb	isc_region_t r;
83143731Sdougb	unsigned int len;
84143731Sdougb
85193149Sdougb	REQUIRE(pub->keydata.dh != NULL);
86193149Sdougb	REQUIRE(priv->keydata.dh != NULL);
87143731Sdougb
88193149Sdougb	dhpub = pub->keydata.dh;
89193149Sdougb	dhpriv = priv->keydata.dh;
90143731Sdougb
91143731Sdougb	len = DH_size(dhpriv);
92143731Sdougb	isc_buffer_availableregion(secret, &r);
93143731Sdougb	if (r.length < len)
94143731Sdougb		return (ISC_R_NOSPACE);
95143731Sdougb	ret = DH_compute_key(r.base, dhpub->pub_key, dhpriv);
96143731Sdougb	if (ret == 0)
97245163Serwin		return (dst__openssl_toresult2("DH_compute_key",
98245163Serwin					       DST_R_COMPUTESECRETFAILURE));
99143731Sdougb	isc_buffer_add(secret, len);
100143731Sdougb	return (ISC_R_SUCCESS);
101143731Sdougb}
102143731Sdougb
103143731Sdougbstatic isc_boolean_t
104143731Sdougbopenssldh_compare(const dst_key_t *key1, const dst_key_t *key2) {
105143731Sdougb	int status;
106143731Sdougb	DH *dh1, *dh2;
107143731Sdougb
108193149Sdougb	dh1 = key1->keydata.dh;
109193149Sdougb	dh2 = key2->keydata.dh;
110143731Sdougb
111143731Sdougb	if (dh1 == NULL && dh2 == NULL)
112143731Sdougb		return (ISC_TRUE);
113143731Sdougb	else if (dh1 == NULL || dh2 == NULL)
114143731Sdougb		return (ISC_FALSE);
115143731Sdougb
116143731Sdougb	status = BN_cmp(dh1->p, dh2->p) ||
117143731Sdougb		 BN_cmp(dh1->g, dh2->g) ||
118143731Sdougb		 BN_cmp(dh1->pub_key, dh2->pub_key);
119143731Sdougb
120143731Sdougb	if (status != 0)
121143731Sdougb		return (ISC_FALSE);
122143731Sdougb
123143731Sdougb	if (dh1->priv_key != NULL || dh2->priv_key != NULL) {
124143731Sdougb		if (dh1->priv_key == NULL || dh2->priv_key == NULL)
125143731Sdougb			return (ISC_FALSE);
126143731Sdougb		if (BN_cmp(dh1->priv_key, dh2->priv_key) != 0)
127143731Sdougb			return (ISC_FALSE);
128143731Sdougb	}
129143731Sdougb	return (ISC_TRUE);
130143731Sdougb}
131143731Sdougb
132143731Sdougbstatic isc_boolean_t
133143731Sdougbopenssldh_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
134143731Sdougb	int status;
135143731Sdougb	DH *dh1, *dh2;
136143731Sdougb
137193149Sdougb	dh1 = key1->keydata.dh;
138193149Sdougb	dh2 = key2->keydata.dh;
139143731Sdougb
140143731Sdougb	if (dh1 == NULL && dh2 == NULL)
141143731Sdougb		return (ISC_TRUE);
142143731Sdougb	else if (dh1 == NULL || dh2 == NULL)
143143731Sdougb		return (ISC_FALSE);
144143731Sdougb
145143731Sdougb	status = BN_cmp(dh1->p, dh2->p) ||
146143731Sdougb		 BN_cmp(dh1->g, dh2->g);
147143731Sdougb
148143731Sdougb	if (status != 0)
149143731Sdougb		return (ISC_FALSE);
150143731Sdougb	return (ISC_TRUE);
151143731Sdougb}
152143731Sdougb
153224092Sdougb#if OPENSSL_VERSION_NUMBER > 0x00908000L
154224092Sdougbstatic int
155224092Sdougbprogress_cb(int p, int n, BN_GENCB *cb)
156224092Sdougb{
157224092Sdougb	union {
158224092Sdougb		void *dptr;
159224092Sdougb		void (*fptr)(int);
160224092Sdougb	} u;
161224092Sdougb
162224092Sdougb	UNUSED(n);
163224092Sdougb
164224092Sdougb	u.dptr = cb->arg;
165224092Sdougb	if (u.fptr != NULL)
166224092Sdougb		u.fptr(p);
167224092Sdougb	return (1);
168224092Sdougb}
169224092Sdougb#endif
170224092Sdougb
171170222Sdougbstatic isc_result_t
172224092Sdougbopenssldh_generate(dst_key_t *key, int generator, void (*callback)(int)) {
173224092Sdougb	DH *dh = NULL;
174170222Sdougb#if OPENSSL_VERSION_NUMBER > 0x00908000L
175193149Sdougb	BN_GENCB cb;
176224092Sdougb	union {
177224092Sdougb		void *dptr;
178224092Sdougb		void (*fptr)(int);
179224092Sdougb	} u;
180224092Sdougb#else
181224092Sdougb
182224092Sdougb	UNUSED(callback);
183165071Sdougb#endif
184143731Sdougb
185143731Sdougb	if (generator == 0) {
186143731Sdougb		if (key->key_size == 768 ||
187143731Sdougb		    key->key_size == 1024 ||
188143731Sdougb		    key->key_size == 1536)
189143731Sdougb		{
190143731Sdougb			dh = DH_new();
191143731Sdougb			if (dh == NULL)
192170222Sdougb				return (dst__openssl_toresult(ISC_R_NOMEMORY));
193143731Sdougb			if (key->key_size == 768)
194143731Sdougb				dh->p = &bn768;
195143731Sdougb			else if (key->key_size == 1024)
196143731Sdougb				dh->p = &bn1024;
197143731Sdougb			else
198143731Sdougb				dh->p = &bn1536;
199143731Sdougb			dh->g = &bn2;
200170222Sdougb		} else
201143731Sdougb			generator = 2;
202143731Sdougb	}
203143731Sdougb
204170222Sdougb	if (generator != 0) {
205170222Sdougb#if OPENSSL_VERSION_NUMBER > 0x00908000L
206170222Sdougb		dh = DH_new();
207170222Sdougb		if (dh == NULL)
208245163Serwin			return (dst__openssl_toresult(ISC_R_NOMEMORY));
209170222Sdougb
210224092Sdougb		if (callback == NULL) {
211224092Sdougb			BN_GENCB_set_old(&cb, NULL, NULL);
212224092Sdougb		} else {
213224092Sdougb			u.fptr = callback;
214224092Sdougb			BN_GENCB_set(&cb, &progress_cb, u.dptr);
215224092Sdougb		}
216170222Sdougb
217170222Sdougb		if (!DH_generate_parameters_ex(dh, key->key_size, generator,
218170222Sdougb					       &cb)) {
219170222Sdougb			DH_free(dh);
220245163Serwin			return (dst__openssl_toresult2(
221245163Serwin					"DH_generate_parameters_ex",
222245163Serwin					DST_R_OPENSSLFAILURE));
223170222Sdougb		}
224170222Sdougb#else
225143731Sdougb		dh = DH_generate_parameters(key->key_size, generator,
226143731Sdougb					    NULL, NULL);
227170222Sdougb#endif
228170222Sdougb	}
229143731Sdougb
230143731Sdougb	if (dh == NULL)
231245163Serwin		return (dst__openssl_toresult2("DH_generate_parameters",
232245163Serwin					       DST_R_OPENSSLFAILURE));
233143731Sdougb
234143731Sdougb	if (DH_generate_key(dh) == 0) {
235143731Sdougb		DH_free(dh);
236245163Serwin		return (dst__openssl_toresult2("DH_generate_key",
237245163Serwin					       DST_R_OPENSSLFAILURE));
238143731Sdougb	}
239143731Sdougb	dh->flags &= ~DH_FLAG_CACHE_MONT_P;
240143731Sdougb
241193149Sdougb	key->keydata.dh = dh;
242143731Sdougb
243143731Sdougb	return (ISC_R_SUCCESS);
244143731Sdougb}
245143731Sdougb
246143731Sdougbstatic isc_boolean_t
247143731Sdougbopenssldh_isprivate(const dst_key_t *key) {
248193149Sdougb	DH *dh = key->keydata.dh;
249143731Sdougb	return (ISC_TF(dh != NULL && dh->priv_key != NULL));
250143731Sdougb}
251143731Sdougb
252143731Sdougbstatic void
253143731Sdougbopenssldh_destroy(dst_key_t *key) {
254193149Sdougb	DH *dh = key->keydata.dh;
255143731Sdougb
256143731Sdougb	if (dh == NULL)
257143731Sdougb		return;
258143731Sdougb
259143731Sdougb	if (dh->p == &bn768 || dh->p == &bn1024 || dh->p == &bn1536)
260143731Sdougb		dh->p = NULL;
261143731Sdougb	if (dh->g == &bn2)
262143731Sdougb		dh->g = NULL;
263143731Sdougb	DH_free(dh);
264193149Sdougb	key->keydata.dh = NULL;
265143731Sdougb}
266143731Sdougb
267143731Sdougbstatic void
268143731Sdougbuint16_toregion(isc_uint16_t val, isc_region_t *region) {
269287410Sdelphij	*region->base = (val & 0xff00) >> 8;
270287410Sdelphij	isc_region_consume(region, 1);
271287410Sdelphij	*region->base = (val & 0x00ff);
272287410Sdelphij	isc_region_consume(region, 1);
273143731Sdougb}
274143731Sdougb
275143731Sdougbstatic isc_uint16_t
276143731Sdougbuint16_fromregion(isc_region_t *region) {
277143731Sdougb	isc_uint16_t val;
278143731Sdougb	unsigned char *cp = region->base;
279143731Sdougb
280143731Sdougb	val = ((unsigned int)(cp[0])) << 8;
281143731Sdougb	val |= ((unsigned int)(cp[1]));
282143731Sdougb
283287410Sdelphij	isc_region_consume(region, 2);
284287410Sdelphij
285143731Sdougb	return (val);
286143731Sdougb}
287143731Sdougb
288143731Sdougbstatic isc_result_t
289143731Sdougbopenssldh_todns(const dst_key_t *key, isc_buffer_t *data) {
290143731Sdougb	DH *dh;
291143731Sdougb	isc_region_t r;
292143731Sdougb	isc_uint16_t dnslen, plen, glen, publen;
293143731Sdougb
294193149Sdougb	REQUIRE(key->keydata.dh != NULL);
295143731Sdougb
296193149Sdougb	dh = key->keydata.dh;
297143731Sdougb
298143731Sdougb	isc_buffer_availableregion(data, &r);
299143731Sdougb
300143731Sdougb	if (dh->g == &bn2 &&
301143731Sdougb	    (dh->p == &bn768 || dh->p == &bn1024 || dh->p == &bn1536)) {
302143731Sdougb		plen = 1;
303143731Sdougb		glen = 0;
304143731Sdougb	}
305143731Sdougb	else {
306143731Sdougb		plen = BN_num_bytes(dh->p);
307143731Sdougb		glen = BN_num_bytes(dh->g);
308143731Sdougb	}
309143731Sdougb	publen = BN_num_bytes(dh->pub_key);
310143731Sdougb	dnslen = plen + glen + publen + 6;
311143731Sdougb	if (r.length < (unsigned int) dnslen)
312143731Sdougb		return (ISC_R_NOSPACE);
313143731Sdougb
314143731Sdougb	uint16_toregion(plen, &r);
315143731Sdougb	if (plen == 1) {
316143731Sdougb		if (dh->p == &bn768)
317143731Sdougb			*r.base = 1;
318143731Sdougb		else if (dh->p == &bn1024)
319143731Sdougb			*r.base = 2;
320143731Sdougb		else
321143731Sdougb			*r.base = 3;
322143731Sdougb	}
323143731Sdougb	else
324143731Sdougb		BN_bn2bin(dh->p, r.base);
325287410Sdelphij	isc_region_consume(&r, plen);
326143731Sdougb
327143731Sdougb	uint16_toregion(glen, &r);
328143731Sdougb	if (glen > 0)
329143731Sdougb		BN_bn2bin(dh->g, r.base);
330287410Sdelphij	isc_region_consume(&r, glen);
331143731Sdougb
332143731Sdougb	uint16_toregion(publen, &r);
333143731Sdougb	BN_bn2bin(dh->pub_key, r.base);
334287410Sdelphij	isc_region_consume(&r, publen);
335143731Sdougb
336143731Sdougb	isc_buffer_add(data, dnslen);
337143731Sdougb
338143731Sdougb	return (ISC_R_SUCCESS);
339143731Sdougb}
340143731Sdougb
341143731Sdougbstatic isc_result_t
342143731Sdougbopenssldh_fromdns(dst_key_t *key, isc_buffer_t *data) {
343143731Sdougb	DH *dh;
344143731Sdougb	isc_region_t r;
345143731Sdougb	isc_uint16_t plen, glen, publen;
346143731Sdougb	int special = 0;
347143731Sdougb
348143731Sdougb	isc_buffer_remainingregion(data, &r);
349143731Sdougb	if (r.length == 0)
350143731Sdougb		return (ISC_R_SUCCESS);
351143731Sdougb
352143731Sdougb	dh = DH_new();
353143731Sdougb	if (dh == NULL)
354170222Sdougb		return (dst__openssl_toresult(ISC_R_NOMEMORY));
355143731Sdougb	dh->flags &= ~DH_FLAG_CACHE_MONT_P;
356143731Sdougb
357143731Sdougb	/*
358143731Sdougb	 * Read the prime length.  1 & 2 are table entries, > 16 means a
359143731Sdougb	 * prime follows, otherwise an error.
360143731Sdougb	 */
361143731Sdougb	if (r.length < 2) {
362143731Sdougb		DH_free(dh);
363143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
364143731Sdougb	}
365143731Sdougb	plen = uint16_fromregion(&r);
366143731Sdougb	if (plen < 16 && plen != 1 && plen != 2) {
367143731Sdougb		DH_free(dh);
368143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
369143731Sdougb	}
370143731Sdougb	if (r.length < plen) {
371143731Sdougb		DH_free(dh);
372143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
373143731Sdougb	}
374143731Sdougb	if (plen == 1 || plen == 2) {
375287410Sdelphij		if (plen == 1) {
376287410Sdelphij			special = *r.base;
377287410Sdelphij			isc_region_consume(&r, 1);
378287410Sdelphij		} else {
379143731Sdougb			special = uint16_fromregion(&r);
380287410Sdelphij		}
381143731Sdougb		switch (special) {
382143731Sdougb			case 1:
383143731Sdougb				dh->p = &bn768;
384143731Sdougb				break;
385143731Sdougb			case 2:
386143731Sdougb				dh->p = &bn1024;
387143731Sdougb				break;
388143731Sdougb			case 3:
389143731Sdougb				dh->p = &bn1536;
390143731Sdougb				break;
391143731Sdougb			default:
392143731Sdougb				DH_free(dh);
393143731Sdougb				return (DST_R_INVALIDPUBLICKEY);
394143731Sdougb		}
395287410Sdelphij	} else {
396143731Sdougb		dh->p = BN_bin2bn(r.base, plen, NULL);
397287410Sdelphij		isc_region_consume(&r, plen);
398143731Sdougb	}
399143731Sdougb
400143731Sdougb	/*
401143731Sdougb	 * Read the generator length.  This should be 0 if the prime was
402143731Sdougb	 * special, but it might not be.  If it's 0 and the prime is not
403143731Sdougb	 * special, we have a problem.
404143731Sdougb	 */
405143731Sdougb	if (r.length < 2) {
406143731Sdougb		DH_free(dh);
407143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
408143731Sdougb	}
409143731Sdougb	glen = uint16_fromregion(&r);
410143731Sdougb	if (r.length < glen) {
411143731Sdougb		DH_free(dh);
412143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
413143731Sdougb	}
414143731Sdougb	if (special != 0) {
415143731Sdougb		if (glen == 0)
416143731Sdougb			dh->g = &bn2;
417143731Sdougb		else {
418143731Sdougb			dh->g = BN_bin2bn(r.base, glen, NULL);
419143731Sdougb			if (BN_cmp(dh->g, &bn2) == 0) {
420143731Sdougb				BN_free(dh->g);
421143731Sdougb				dh->g = &bn2;
422143731Sdougb			}
423143731Sdougb			else {
424143731Sdougb				DH_free(dh);
425143731Sdougb				return (DST_R_INVALIDPUBLICKEY);
426143731Sdougb			}
427143731Sdougb		}
428287410Sdelphij	} else {
429143731Sdougb		if (glen == 0) {
430143731Sdougb			DH_free(dh);
431143731Sdougb			return (DST_R_INVALIDPUBLICKEY);
432143731Sdougb		}
433143731Sdougb		dh->g = BN_bin2bn(r.base, glen, NULL);
434143731Sdougb	}
435287410Sdelphij	isc_region_consume(&r, glen);
436143731Sdougb
437143731Sdougb	if (r.length < 2) {
438143731Sdougb		DH_free(dh);
439143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
440143731Sdougb	}
441143731Sdougb	publen = uint16_fromregion(&r);
442143731Sdougb	if (r.length < publen) {
443143731Sdougb		DH_free(dh);
444143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
445143731Sdougb	}
446143731Sdougb	dh->pub_key = BN_bin2bn(r.base, publen, NULL);
447287410Sdelphij	isc_region_consume(&r, publen);
448143731Sdougb
449143731Sdougb	key->key_size = BN_num_bits(dh->p);
450143731Sdougb
451143731Sdougb	isc_buffer_forward(data, plen + glen + publen + 6);
452143731Sdougb
453193149Sdougb	key->keydata.dh = dh;
454143731Sdougb
455143731Sdougb	return (ISC_R_SUCCESS);
456143731Sdougb}
457143731Sdougb
458143731Sdougbstatic isc_result_t
459143731Sdougbopenssldh_tofile(const dst_key_t *key, const char *directory) {
460143731Sdougb	int i;
461143731Sdougb	DH *dh;
462143731Sdougb	dst_private_t priv;
463143731Sdougb	unsigned char *bufs[4];
464143731Sdougb	isc_result_t result;
465143731Sdougb
466193149Sdougb	if (key->keydata.dh == NULL)
467143731Sdougb		return (DST_R_NULLKEY);
468143731Sdougb
469193149Sdougb	dh = key->keydata.dh;
470143731Sdougb
471245163Serwin	memset(bufs, 0, sizeof(bufs));
472143731Sdougb	for (i = 0; i < 4; i++) {
473143731Sdougb		bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(dh->p));
474143731Sdougb		if (bufs[i] == NULL) {
475143731Sdougb			result = ISC_R_NOMEMORY;
476143731Sdougb			goto fail;
477143731Sdougb		}
478143731Sdougb	}
479143731Sdougb
480143731Sdougb	i = 0;
481143731Sdougb
482143731Sdougb	priv.elements[i].tag = TAG_DH_PRIME;
483143731Sdougb	priv.elements[i].length = BN_num_bytes(dh->p);
484143731Sdougb	BN_bn2bin(dh->p, bufs[i]);
485143731Sdougb	priv.elements[i].data = bufs[i];
486143731Sdougb	i++;
487143731Sdougb
488143731Sdougb	priv.elements[i].tag = TAG_DH_GENERATOR;
489143731Sdougb	priv.elements[i].length = BN_num_bytes(dh->g);
490143731Sdougb	BN_bn2bin(dh->g, bufs[i]);
491143731Sdougb	priv.elements[i].data = bufs[i];
492143731Sdougb	i++;
493143731Sdougb
494143731Sdougb	priv.elements[i].tag = TAG_DH_PRIVATE;
495143731Sdougb	priv.elements[i].length = BN_num_bytes(dh->priv_key);
496143731Sdougb	BN_bn2bin(dh->priv_key, bufs[i]);
497143731Sdougb	priv.elements[i].data = bufs[i];
498143731Sdougb	i++;
499143731Sdougb
500143731Sdougb	priv.elements[i].tag = TAG_DH_PUBLIC;
501143731Sdougb	priv.elements[i].length = BN_num_bytes(dh->pub_key);
502143731Sdougb	BN_bn2bin(dh->pub_key, bufs[i]);
503143731Sdougb	priv.elements[i].data = bufs[i];
504143731Sdougb	i++;
505143731Sdougb
506143731Sdougb	priv.nelements = i;
507143731Sdougb	result = dst__privstruct_writefile(key, &priv, directory);
508143731Sdougb fail:
509143731Sdougb	for (i = 0; i < 4; i++) {
510143731Sdougb		if (bufs[i] == NULL)
511143731Sdougb			break;
512143731Sdougb		isc_mem_put(key->mctx, bufs[i], BN_num_bytes(dh->p));
513143731Sdougb	}
514143731Sdougb	return (result);
515143731Sdougb}
516143731Sdougb
517143731Sdougbstatic isc_result_t
518224092Sdougbopenssldh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
519143731Sdougb	dst_private_t priv;
520143731Sdougb	isc_result_t ret;
521143731Sdougb	int i;
522143731Sdougb	DH *dh = NULL;
523143731Sdougb	isc_mem_t *mctx;
524143731Sdougb#define DST_RET(a) {ret = a; goto err;}
525143731Sdougb
526224092Sdougb	UNUSED(pub);
527143731Sdougb	mctx = key->mctx;
528143731Sdougb
529143731Sdougb	/* read private key file */
530143731Sdougb	ret = dst__privstruct_parse(key, DST_ALG_DH, lexer, mctx, &priv);
531143731Sdougb	if (ret != ISC_R_SUCCESS)
532143731Sdougb		return (ret);
533143731Sdougb
534143731Sdougb	dh = DH_new();
535143731Sdougb	if (dh == NULL)
536143731Sdougb		DST_RET(ISC_R_NOMEMORY);
537143731Sdougb	dh->flags &= ~DH_FLAG_CACHE_MONT_P;
538193149Sdougb	key->keydata.dh = dh;
539143731Sdougb
540143731Sdougb	for (i = 0; i < priv.nelements; i++) {
541143731Sdougb		BIGNUM *bn;
542143731Sdougb		bn = BN_bin2bn(priv.elements[i].data,
543143731Sdougb			       priv.elements[i].length, NULL);
544143731Sdougb		if (bn == NULL)
545143731Sdougb			DST_RET(ISC_R_NOMEMORY);
546143731Sdougb
547143731Sdougb		switch (priv.elements[i].tag) {
548143731Sdougb			case TAG_DH_PRIME:
549143731Sdougb				dh->p = bn;
550143731Sdougb				break;
551143731Sdougb			case TAG_DH_GENERATOR:
552143731Sdougb				dh->g = bn;
553143731Sdougb				break;
554143731Sdougb			case TAG_DH_PRIVATE:
555143731Sdougb				dh->priv_key = bn;
556143731Sdougb				break;
557143731Sdougb			case TAG_DH_PUBLIC:
558143731Sdougb				dh->pub_key = bn;
559143731Sdougb				break;
560143731Sdougb		}
561143731Sdougb	}
562143731Sdougb	dst__privstruct_free(&priv, mctx);
563143731Sdougb
564143731Sdougb	key->key_size = BN_num_bits(dh->p);
565143731Sdougb
566143731Sdougb	if ((key->key_size == 768 ||
567143731Sdougb	     key->key_size == 1024 ||
568143731Sdougb	     key->key_size == 1536) &&
569143731Sdougb	    BN_cmp(dh->g, &bn2) == 0)
570143731Sdougb	{
571143731Sdougb		if (key->key_size == 768 && BN_cmp(dh->p, &bn768) == 0) {
572143731Sdougb			BN_free(dh->p);
573143731Sdougb			BN_free(dh->g);
574143731Sdougb			dh->p = &bn768;
575143731Sdougb			dh->g = &bn2;
576143731Sdougb		} else if (key->key_size == 1024 &&
577143731Sdougb			   BN_cmp(dh->p, &bn1024) == 0) {
578143731Sdougb			BN_free(dh->p);
579143731Sdougb			BN_free(dh->g);
580143731Sdougb			dh->p = &bn1024;
581143731Sdougb			dh->g = &bn2;
582143731Sdougb		} else if (key->key_size == 1536 &&
583143731Sdougb			   BN_cmp(dh->p, &bn1536) == 0) {
584143731Sdougb			BN_free(dh->p);
585143731Sdougb			BN_free(dh->g);
586143731Sdougb			dh->p = &bn1536;
587143731Sdougb			dh->g = &bn2;
588143731Sdougb		}
589143731Sdougb	}
590143731Sdougb
591143731Sdougb	return (ISC_R_SUCCESS);
592143731Sdougb
593143731Sdougb err:
594143731Sdougb	openssldh_destroy(key);
595143731Sdougb	dst__privstruct_free(&priv, mctx);
596143731Sdougb	memset(&priv, 0, sizeof(priv));
597143731Sdougb	return (ret);
598143731Sdougb}
599143731Sdougb
600143731Sdougbstatic void
601143731SdougbBN_fromhex(BIGNUM *b, const char *str) {
602143731Sdougb	static const char hexdigits[] = "0123456789abcdef";
603143731Sdougb	unsigned char data[512];
604143731Sdougb	unsigned int i;
605143731Sdougb	BIGNUM *out;
606143731Sdougb
607143731Sdougb	RUNTIME_CHECK(strlen(str) < 1024U && strlen(str) % 2 == 0U);
608143731Sdougb	for (i = 0; i < strlen(str); i += 2) {
609143731Sdougb		char *s;
610143731Sdougb		unsigned int high, low;
611143731Sdougb
612143731Sdougb		s = strchr(hexdigits, tolower((unsigned char)str[i]));
613143731Sdougb		RUNTIME_CHECK(s != NULL);
614262706Serwin		high = (unsigned int)(s - hexdigits);
615143731Sdougb
616143731Sdougb		s = strchr(hexdigits, tolower((unsigned char)str[i + 1]));
617143731Sdougb		RUNTIME_CHECK(s != NULL);
618262706Serwin		low = (unsigned int)(s - hexdigits);
619143731Sdougb
620143731Sdougb		data[i/2] = (unsigned char)((high << 4) + low);
621143731Sdougb	}
622143731Sdougb	out = BN_bin2bn(data, strlen(str)/2, b);
623143731Sdougb	RUNTIME_CHECK(out != NULL);
624143731Sdougb}
625143731Sdougb
626143731Sdougbstatic void
627143731Sdougbopenssldh_cleanup(void) {
628143731Sdougb	BN_free(&bn2);
629143731Sdougb	BN_free(&bn768);
630143731Sdougb	BN_free(&bn1024);
631143731Sdougb	BN_free(&bn1536);
632143731Sdougb}
633143731Sdougb
634143731Sdougbstatic dst_func_t openssldh_functions = {
635170222Sdougb	NULL, /*%< createctx */
636170222Sdougb	NULL, /*%< destroyctx */
637170222Sdougb	NULL, /*%< adddata */
638170222Sdougb	NULL, /*%< openssldh_sign */
639170222Sdougb	NULL, /*%< openssldh_verify */
640254897Serwin	NULL, /*%< openssldh_verify2 */
641143731Sdougb	openssldh_computesecret,
642143731Sdougb	openssldh_compare,
643143731Sdougb	openssldh_paramcompare,
644143731Sdougb	openssldh_generate,
645143731Sdougb	openssldh_isprivate,
646143731Sdougb	openssldh_destroy,
647143731Sdougb	openssldh_todns,
648143731Sdougb	openssldh_fromdns,
649143731Sdougb	openssldh_tofile,
650143731Sdougb	openssldh_parse,
651143731Sdougb	openssldh_cleanup,
652193149Sdougb	NULL, /*%< fromlabel */
653224092Sdougb	NULL, /*%< dump */
654224092Sdougb	NULL, /*%< restore */
655143731Sdougb};
656143731Sdougb
657143731Sdougbisc_result_t
658143731Sdougbdst__openssldh_init(dst_func_t **funcp) {
659143731Sdougb	REQUIRE(funcp != NULL);
660143731Sdougb	if (*funcp == NULL) {
661143731Sdougb		BN_init(&bn2);
662143731Sdougb		BN_init(&bn768);
663143731Sdougb		BN_init(&bn1024);
664143731Sdougb		BN_init(&bn1536);
665143731Sdougb		BN_set_word(&bn2, 2);
666143731Sdougb		BN_fromhex(&bn768, PRIME768);
667143731Sdougb		BN_fromhex(&bn1024, PRIME1024);
668143731Sdougb		BN_fromhex(&bn1536, PRIME1536);
669143731Sdougb		*funcp = &openssldh_functions;
670143731Sdougb	}
671143731Sdougb	return (ISC_R_SUCCESS);
672143731Sdougb}
673143731Sdougb
674143731Sdougb#else /* OPENSSL */
675143731Sdougb
676143731Sdougb#include <isc/util.h>
677143731Sdougb
678143731SdougbEMPTY_TRANSLATION_UNIT
679143731Sdougb
680143731Sdougb#endif /* OPENSSL */
681170222Sdougb/*! \file */
682