1/*
2 * Copyright (c) 2011-12 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "ossl-config.h"
25
26#include <stdio.h>
27#include <stdlib.h>
28
29#include "rfc2459_asn1.h"
30
31#include "ossl-dh.h"
32#include "ossl-common.h"
33
34#ifdef HAVE_COMMONCRYPTO_COMMONDH_H
35
36
37/* XXX <rdar://problem/10771188> is blocking this code from working */
38#ifdef PR_10771188_FIXED
39#include <CommonCrypto/CommonDH.h>
40#include <CommonCrypto/CommonBigNum.h>
41
42/*
43 * XXX we need the following structs from CommonDHPriv.h
44 * given the missing bits in the CommonDH SPI.  See the
45 * other XXX's below for the details.
46 */
47typedef struct DHParms_ {
48	CCBigNumRef	p;
49	CCBigNumRef	g;
50	uint32_t	l;
51} DHParmSet;
52
53typedef struct DH_ {
54	DHParmSet	parms;
55	CCBigNumRef	pub_key;
56	CCBigNumRef	priv_key;
57} *CCDH;
58
59/*
60 *
61 */
62static /* CCHRef */ CCDH
63loadkeys(DH *dh)
64{
65	void *p = NULL, *g = NULL, *priv_key = NULL, *pub_key = NULL;
66	int plen, glen, priv_keylen, pub_keylen;
67	/* CCHRef */ CCDH ccdh = NULL;
68	CCDHParameters dhparams = NULL;
69
70	plen = BN_num_bytes(dh->p);
71	glen = BN_num_bytes(dh->g);
72
73	p = malloc(plen);
74	g = malloc(glen);
75	if ((NULL == p) || (NULL == g)) {
76		goto err;
77	}
78
79	if (!BN_bn2bin(dh->p, (unsigned char *)p)) {
80		goto err;
81	}
82	if (!BN_bn2bin(dh->g, (unsigned char *)g)) {
83		goto err;
84	}
85
86	dhparams = CCDHCreateParametersFromData(p, plen, g, glen);
87	if (NULL == dhparams) {
88		goto err;
89	}
90
91	ccdh = (CCDH)CCDHCreate(dhparams);
92	if (NULL == ccdh) {
93		goto err;
94	}
95
96	/*
97	 * XXX doesn't properly initialize priv_key and pub_key.
98	 * See <rdar://problem/10771223> for more information.
99	 * The following is the workaround for now.
100	 */
101	ccdh->pub_key = NULL;
102	ccdh->priv_key = NULL;
103
104	/* XXX there is no SPI call to use a provided private key
105	 * See <rdar://problem/10771283> for more info.
106	 */
107	if (NULL != dh->priv_key) {
108		CCStatus bnstatus;
109
110		/* we have a private key provided */
111		priv_keylen = BN_num_bytes(dh->priv_key);
112		priv_key = malloc(priv_keylen);
113		if (NULL == priv_key) {
114			goto err;
115		}
116		if (!BN_bn2bin(dh->priv_key, (unsigned char *)priv_key)) {
117			goto err;
118		}
119		if ((ccdh->priv_key = CCBigNumFromData(&bnstatus,
120		    priv_key, priv_keylen)) == NULL) {
121			goto err;
122		}
123	}
124
125	/* XXX there is no SPI call to use a provided public key
126	 * See <rdar://problem/10771283> for more info.
127	 */
128	if (NULL != dh->pub_key) {
129		CCStatus bnstatus;
130
131		/* we have a private key provided */
132		pub_keylen = BN_num_bytes(dh->pub_key);
133		pub_key = malloc(pub_keylen);
134		if (NULL == pub_key) {
135			goto err;
136		}
137		if (!BN_bn2bin(dh->pub_key, (unsigned char *)pub_key)) {
138			goto err;
139		}
140		if ((ccdh->pub_key = CCBigNumFromData(&bnstatus,
141		    pub_key, pub_keylen)) == NULL) {
142			goto err;
143		}
144	}
145
146err:
147	if (p) {
148		memset(p, 0, plen);
149		free(p);
150	}
151	if (g) {
152		memset(g, 0, glen);
153		free(g);
154	}
155	if (priv_key) {
156		memset(g, 0, priv_keylen);
157		free(priv_key);
158	}
159	if (pub_key) {
160		memset(g, 0, pub_keylen);
161		free(pub_key);
162	}
163	if (dhparams) {
164		CCDHParametersRelease(dhparams);
165	}
166
167	return (ccdh);
168}
169
170
171static int
172cc_dh_generate_key(DH *dh)
173{
174	/* CCDHRef */
175	CCDH ccdh = NULL;
176	size_t pub_keylen;
177	void *pub_key;
178	int rv = 0;
179
180	if ((NULL == dh->p) || (NULL == dh->g)) {
181		return (0);
182	}
183
184	if ((ccdh = loadkeys(dh)) == NULL) {
185		goto err;
186	}
187
188	/* XXX header doc for CCDHGenerateKey is wrong.
189	 * output is bin data not BigNUm.
190	 * *outputLength = CCBigNumToData(&bnRetval, dh->pub_key, output);
191	 * See <rdar://problem/10771260>
192	 */
193	if (CCDHGenerateKey(ccdh, pub_key, &pub_keylen) != 0) {
194		goto err;
195	}
196
197	if ((dh->pub_key = BN_bin2bn(pub_key, (int)pub_keylen, dh->pub_key))
198	    == NULL) {
199		goto err;
200	}
201
202	rv = 1;
203
204err:
205	if (ccdh) {
206		/*
207		 * XXX CCDHRelease() doesn't free pub/priv key.
208		 * See <rdar://problem/10771265> for more info.
209		 */
210		if (ccdh->priv_key) {
211			CCBigNumFree(ccdh->priv_key);
212		}
213		if (ccdh->pub_key) {
214			CCBigNumFree(ccdh->pub_key);
215		}
216
217		CCDHRelease((CCDHRef)ccdh);
218	}
219
220	return (rv);
221}
222
223
224static int
225cc_dh_compute_key(unsigned char *shared, const BIGNUM *peer, DH *dh)
226{
227	/* CCDHRef */
228	CCDH ccdh = NULL;
229	void *peer_key = NULL;
230	int rv = -1, peer_keylen;
231
232
233	if ((NULL == dh->priv_key) || (NULL == dh->pub_key)) {
234		return (-1);
235	}
236
237	if ((ccdh = loadkeys(dh)) == NULL) {
238		goto err;
239	}
240
241	peer_keylen = BN_num_bytes(peer);
242	if ((peer_key = malloc(peer_keylen)) == NULL) {
243		goto err;
244	}
245	if (!BN_bn2bin(peer, (unsigned char *)peer_key)) {
246		goto err;
247	}
248
249	if ((rv = CCDHComputeKey(shared, peer_key, (size_t)peer_keylen,
250	    (CCDHRef)ccdh)) == -1) {
251		goto err;
252	}
253
254err:
255	if (ccdh) {
256		/* XXX CCDHRelease() doesn't free pub/priv key */
257		if (ccdh->priv_key) {
258			CCBigNumFree(ccdh->priv_key);
259		}
260		if (ccdh->pub_key) {
261			CCBigNumFree(ccdh->pub_key);
262		}
263
264		CCDHRelease((CCDHRef)ccdh);
265	}
266
267	return (rv);
268}
269
270
271#else /* ! PR_10771188_FIXED */
272
273/*
274 * Given that CC's implementation of DH doesn't work we use libtommath.
275 * Note that this requires the libtommath lib to be linked in.
276 */
277#include <tommath.h>
278
279static void
280BN2mpz(mp_int *s, const BIGNUM *bn)
281{
282	size_t len;
283	void *p;
284
285	len = BN_num_bytes(bn);
286	p = malloc(len);
287	BN_bn2bin(bn, p);
288	mp_read_unsigned_bin(s, p, len);
289	/* s = (mp_int *)CCBigNumFromData(&status, p, len); */
290	free(p);
291}
292
293
294static BIGNUM *
295mpz2BN(mp_int *s)
296{
297	size_t size;
298	BIGNUM *bn;
299	void *p;
300
301	size = mp_unsigned_bin_size(s);
302	p = malloc(size);
303	if ((p == NULL) && (size != 0)) {
304		return (NULL);
305	}
306	mp_to_unsigned_bin(s, p);
307	/* (void)CCBigNumToData(&status, (CCBigNumRef)s, p); */
308
309	bn = BN_bin2bn(p, size, NULL);
310	free(p);
311	return (bn);
312}
313
314
315/*
316 *
317 */
318
319#define DH_NUM_TRIES    10
320
321static int
322cc_dh_generate_key(DH *dh)
323{
324	mp_int pub, priv_key, g, p;
325	int have_private_key = (dh->priv_key != NULL);
326	int codes, times = 0;
327	int res;
328
329	if ((dh->p == NULL) || (dh->g == NULL)) {
330		return (0);
331	}
332
333	mp_init_multi(&pub, &priv_key, &g, &p, NULL);
334
335	while (times++ < DH_NUM_TRIES) {
336		if (!have_private_key) {
337			size_t bits = BN_num_bits(dh->p);
338
339			if (dh->priv_key) {
340				BN_free(dh->priv_key);
341			}
342
343			dh->priv_key = BN_new();
344			if (dh->priv_key == NULL) {
345				return (0);
346			}
347			if (!BN_rand(dh->priv_key, bits /* - 1 */, 0, 0)) {
348				BN_clear_free(dh->priv_key);
349				dh->priv_key = NULL;
350				return (0);
351			}
352		}
353		if (dh->pub_key) {
354			BN_free(dh->pub_key);
355		}
356
357		/* mp_init_multi(&pub, &priv_key, &g, &p, NULL); */
358
359		BN2mpz(&g, dh->g);
360		BN2mpz(&priv_key, dh->priv_key);
361		BN2mpz(&p, dh->p);
362
363		res = mp_exptmod(&g, &priv_key, &p, &pub);
364
365		mp_clear_multi(&priv_key, &g, &p, NULL);
366		if (res != 0) {
367			continue;
368		}
369
370		dh->pub_key = mpz2BN(&pub);
371		mp_clear(&pub);
372		if (dh->pub_key == NULL) {
373			return (0);
374		}
375
376		if (DH_check_pubkey(dh, dh->pub_key, &codes) && (codes == 0)) {
377			break;
378		}
379		if (have_private_key) {
380			return (0);
381		}
382	}
383
384	if (times >= DH_NUM_TRIES) {
385		if (!have_private_key && dh->priv_key) {
386			BN_free(dh->priv_key);
387			dh->priv_key = NULL;
388		}
389		if (dh->pub_key) {
390			BN_free(dh->pub_key);
391			dh->pub_key = NULL;
392		}
393		return (0);
394	}
395
396	return (1);
397}
398
399
400#define mp_isneg(a)    (((a)->sign) ? 1 : 0)
401
402static int
403cc_dh_compute_key(unsigned char *shared, const BIGNUM *pub, DH *dh)
404{
405	mp_int s, priv_key, p, peer_pub;
406	int ret;
407
408	if ((dh->pub_key == NULL) || (dh->g == NULL) || (dh->priv_key == NULL)) {
409		return (-1);
410	}
411
412	mp_init_multi(&s, &priv_key, &p, &peer_pub, NULL);
413
414	BN2mpz(&p, dh->p);
415	BN2mpz(&peer_pub, pub);
416
417	/* check if peers pubkey is reasonable */
418	if (mp_isneg(&peer_pub) ||
419	    (mp_cmp(&peer_pub, &p) >= 0) ||
420	    (mp_cmp_d(&peer_pub, 1) <= 0)) {
421		ret = -1;
422		goto out;
423	}
424
425	BN2mpz(&priv_key, dh->priv_key);
426
427	ret = mp_exptmod(&peer_pub, &priv_key, &p, &s);
428	if (ret != 0) {
429		ret = -1;
430		goto out;
431	}
432
433	ret = mp_unsigned_bin_size(&s);
434	mp_to_unsigned_bin(&s, shared);
435
436out:
437	mp_clear_multi(&s, &priv_key, &p, &peer_pub, NULL);
438
439	return (ret);
440}
441
442
443#endif /* ! PR_10771188_FIXED */
444
445static int
446cc_dh_generate_params(DH *dh, int a, int b, BN_GENCB *callback)
447{
448	/* groups should already be known, we don't care about this */
449	return (0);
450}
451
452
453static int
454cc_dh_init(DH *dh)
455{
456	/* set flags */
457	return (1);
458}
459
460
461static int
462cc_dh_finish(DH *dh)
463{
464	/* free resources */
465	return (1);
466}
467
468
469/*
470 *
471 */
472
473const DH_METHOD _ossl_dh_cc_method =
474{
475	.name			= "CommonCrypto DH",
476	.generate_key		= cc_dh_generate_key,
477	.compute_key		= cc_dh_compute_key,
478	.bn_mod_exp		= NULL,
479	.init			= cc_dh_init,
480	.finish			= cc_dh_finish,
481	.flags			=	    0,
482	.app_data		= NULL,
483	.generate_params	= cc_dh_generate_params
484};
485
486#endif /* HAVE_COMMONCRYPTO_COMMONDH_H */
487
488/**
489 * DH implementation using cdsa.
490 *
491 * @return the DH_METHOD for the DH implementation using CommonCrypto.
492 */
493const DH_METHOD *
494DH_cc_method(void)
495{
496#ifdef HAVE_COMMONCRYPTO_COMMONDH_H
497	return (&_ossl_dh_cc_method);
498
499#else
500	return (NULL);
501#endif  /* HAVE_COMMONCRYPTO_COMMONDH_H */
502}
503