xref: /titanic_50/usr/src/cmd/ssh/libssh/common/dh.c (revision cd7d5faf5bbb52336a6f85578a90b31a648ac3fa)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright (c) 2000 Niels Provos.  All rights reserved.
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
57c478bd9Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
67c478bd9Sstevel@tonic-gate  * are met:
77c478bd9Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
87c478bd9Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
97c478bd9Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
107c478bd9Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
117c478bd9Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
147c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
157c478bd9Sstevel@tonic-gate  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
167c478bd9Sstevel@tonic-gate  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
177c478bd9Sstevel@tonic-gate  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
187c478bd9Sstevel@tonic-gate  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
197c478bd9Sstevel@tonic-gate  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
207c478bd9Sstevel@tonic-gate  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
217c478bd9Sstevel@tonic-gate  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
227c478bd9Sstevel@tonic-gate  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate #include "includes.h"
267c478bd9Sstevel@tonic-gate RCSID("$OpenBSD: dh.c,v 1.22 2002/06/27 08:49:44 markus Exp $");
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include "xmalloc.h"
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <openssl/bn.h>
317c478bd9Sstevel@tonic-gate #include <openssl/dh.h>
327c478bd9Sstevel@tonic-gate #include <openssl/evp.h>
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include "buffer.h"
357c478bd9Sstevel@tonic-gate #include "cipher.h"
367c478bd9Sstevel@tonic-gate #include "kex.h"
377c478bd9Sstevel@tonic-gate #include "dh.h"
387c478bd9Sstevel@tonic-gate #include "pathnames.h"
397c478bd9Sstevel@tonic-gate #include "log.h"
407c478bd9Sstevel@tonic-gate #include "misc.h"
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate static int
parse_prime(int linenum,char * line,struct dhgroup * dhg)437c478bd9Sstevel@tonic-gate parse_prime(int linenum, char *line, struct dhgroup *dhg)
447c478bd9Sstevel@tonic-gate {
457c478bd9Sstevel@tonic-gate 	char *cp, *arg;
467c478bd9Sstevel@tonic-gate 	char *strsize, *gen, *prime;
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate 	cp = line;
497c478bd9Sstevel@tonic-gate 	arg = strdelim(&cp);
507c478bd9Sstevel@tonic-gate 	/* Ignore leading whitespace */
517c478bd9Sstevel@tonic-gate 	if (*arg == '\0')
527c478bd9Sstevel@tonic-gate 		arg = strdelim(&cp);
537c478bd9Sstevel@tonic-gate 	if (!arg || !*arg || *arg == '#')
547c478bd9Sstevel@tonic-gate 		return 0;
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate 	/* time */
577c478bd9Sstevel@tonic-gate 	if (cp == NULL || *arg == '\0')
587c478bd9Sstevel@tonic-gate 		goto fail;
597c478bd9Sstevel@tonic-gate 	arg = strsep(&cp, " "); /* type */
607c478bd9Sstevel@tonic-gate 	if (cp == NULL || *arg == '\0')
617c478bd9Sstevel@tonic-gate 		goto fail;
627c478bd9Sstevel@tonic-gate 	arg = strsep(&cp, " "); /* tests */
637c478bd9Sstevel@tonic-gate 	if (cp == NULL || *arg == '\0')
647c478bd9Sstevel@tonic-gate 		goto fail;
657c478bd9Sstevel@tonic-gate 	arg = strsep(&cp, " "); /* tries */
667c478bd9Sstevel@tonic-gate 	if (cp == NULL || *arg == '\0')
677c478bd9Sstevel@tonic-gate 		goto fail;
687c478bd9Sstevel@tonic-gate 	strsize = strsep(&cp, " "); /* size */
697c478bd9Sstevel@tonic-gate 	if (cp == NULL || *strsize == '\0' ||
707c478bd9Sstevel@tonic-gate 	    (dhg->size = atoi(strsize)) == 0)
717c478bd9Sstevel@tonic-gate 		goto fail;
727c478bd9Sstevel@tonic-gate 	/* The whole group is one bit larger */
737c478bd9Sstevel@tonic-gate 	dhg->size++;
747c478bd9Sstevel@tonic-gate 	gen = strsep(&cp, " "); /* gen */
757c478bd9Sstevel@tonic-gate 	if (cp == NULL || *gen == '\0')
767c478bd9Sstevel@tonic-gate 		goto fail;
777c478bd9Sstevel@tonic-gate 	prime = strsep(&cp, " "); /* prime */
787c478bd9Sstevel@tonic-gate 	if (cp != NULL || *prime == '\0')
797c478bd9Sstevel@tonic-gate 		goto fail;
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 	if ((dhg->g = BN_new()) == NULL)
827c478bd9Sstevel@tonic-gate 		fatal("parse_prime: BN_new failed");
837c478bd9Sstevel@tonic-gate 	if ((dhg->p = BN_new()) == NULL)
847c478bd9Sstevel@tonic-gate 		fatal("parse_prime: BN_new failed");
857c478bd9Sstevel@tonic-gate 	if (BN_hex2bn(&dhg->g, gen) == 0)
867c478bd9Sstevel@tonic-gate 		goto failclean;
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate 	if (BN_hex2bn(&dhg->p, prime) == 0)
897c478bd9Sstevel@tonic-gate 		goto failclean;
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate 	if (BN_num_bits(dhg->p) != dhg->size)
927c478bd9Sstevel@tonic-gate 		goto failclean;
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate 	return (1);
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate  failclean:
977c478bd9Sstevel@tonic-gate 	BN_clear_free(dhg->g);
987c478bd9Sstevel@tonic-gate 	BN_clear_free(dhg->p);
997c478bd9Sstevel@tonic-gate  fail:
1007c478bd9Sstevel@tonic-gate 	error("Bad prime description in line %d", linenum);
1017c478bd9Sstevel@tonic-gate 	return (0);
1027c478bd9Sstevel@tonic-gate }
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate DH *
choose_dh(int min,int wantbits,int max)1057c478bd9Sstevel@tonic-gate choose_dh(int min, int wantbits, int max)
1067c478bd9Sstevel@tonic-gate {
1077c478bd9Sstevel@tonic-gate 	FILE *f;
1087c478bd9Sstevel@tonic-gate 	char line[2048];
1097c478bd9Sstevel@tonic-gate 	int best, bestcount, which;
1107c478bd9Sstevel@tonic-gate 	int linenum;
1117c478bd9Sstevel@tonic-gate 	struct dhgroup dhg;
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate 	if ((f = fopen(_PATH_DH_MODULI, "r")) == NULL &&
1147c478bd9Sstevel@tonic-gate 	    (f = fopen(_PATH_DH_PRIMES, "r")) == NULL) {
1157c478bd9Sstevel@tonic-gate 		log("WARNING: %s does not exist, using old modulus", _PATH_DH_MODULI);
1167c478bd9Sstevel@tonic-gate 		return (dh_new_group1());
1177c478bd9Sstevel@tonic-gate 	}
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	linenum = 0;
1207c478bd9Sstevel@tonic-gate 	best = bestcount = 0;
1217c478bd9Sstevel@tonic-gate 	while (fgets(line, sizeof(line), f)) {
1227c478bd9Sstevel@tonic-gate 		linenum++;
1237c478bd9Sstevel@tonic-gate 		if (!parse_prime(linenum, line, &dhg))
1247c478bd9Sstevel@tonic-gate 			continue;
1257c478bd9Sstevel@tonic-gate 		BN_clear_free(dhg.g);
1267c478bd9Sstevel@tonic-gate 		BN_clear_free(dhg.p);
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 		if (dhg.size > max || dhg.size < min)
1297c478bd9Sstevel@tonic-gate 			continue;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 		if ((dhg.size > wantbits && dhg.size < best) ||
1327c478bd9Sstevel@tonic-gate 		    (dhg.size > best && best < wantbits)) {
1337c478bd9Sstevel@tonic-gate 			best = dhg.size;
1347c478bd9Sstevel@tonic-gate 			bestcount = 0;
1357c478bd9Sstevel@tonic-gate 		}
1367c478bd9Sstevel@tonic-gate 		if (dhg.size == best)
1377c478bd9Sstevel@tonic-gate 			bestcount++;
1387c478bd9Sstevel@tonic-gate 	}
1397c478bd9Sstevel@tonic-gate 	rewind(f);
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 	if (bestcount == 0) {
1427c478bd9Sstevel@tonic-gate 		fclose(f);
1437c478bd9Sstevel@tonic-gate 		log("WARNING: no suitable primes in %s", _PATH_DH_PRIMES);
1447c478bd9Sstevel@tonic-gate 		return (NULL);
1457c478bd9Sstevel@tonic-gate 	}
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	linenum = 0;
1487c478bd9Sstevel@tonic-gate 	which = arc4random() % bestcount;
1497c478bd9Sstevel@tonic-gate 	while (fgets(line, sizeof(line), f)) {
1507c478bd9Sstevel@tonic-gate 		if (!parse_prime(linenum, line, &dhg))
1517c478bd9Sstevel@tonic-gate 			continue;
1527c478bd9Sstevel@tonic-gate 		if ((dhg.size > max || dhg.size < min) ||
1537c478bd9Sstevel@tonic-gate 		    dhg.size != best ||
1547c478bd9Sstevel@tonic-gate 		    linenum++ != which) {
1557c478bd9Sstevel@tonic-gate 			BN_clear_free(dhg.g);
1567c478bd9Sstevel@tonic-gate 			BN_clear_free(dhg.p);
1577c478bd9Sstevel@tonic-gate 			continue;
1587c478bd9Sstevel@tonic-gate 		}
1597c478bd9Sstevel@tonic-gate 		break;
1607c478bd9Sstevel@tonic-gate 	}
1617c478bd9Sstevel@tonic-gate 	fclose(f);
1627c478bd9Sstevel@tonic-gate 	if (linenum != which+1)
1637c478bd9Sstevel@tonic-gate 		fatal("WARNING: line %d disappeared in %s, giving up",
1647c478bd9Sstevel@tonic-gate 		    which, _PATH_DH_PRIMES);
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 	return (dh_new_group(dhg.g, dhg.p));
1677c478bd9Sstevel@tonic-gate }
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate /* diffie-hellman-group1-sha1 */
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate int
dh_pub_is_valid(DH * dh,BIGNUM * dh_pub)1727c478bd9Sstevel@tonic-gate dh_pub_is_valid(DH *dh, BIGNUM *dh_pub)
1737c478bd9Sstevel@tonic-gate {
1747c478bd9Sstevel@tonic-gate 	int i;
1757c478bd9Sstevel@tonic-gate 	int n = BN_num_bits(dh_pub);
1767c478bd9Sstevel@tonic-gate 	int bits_set = 0;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 	if (dh_pub->neg) {
1797c478bd9Sstevel@tonic-gate 		log("invalid public DH value: negativ");
1807c478bd9Sstevel@tonic-gate 		return 0;
1817c478bd9Sstevel@tonic-gate 	}
1827c478bd9Sstevel@tonic-gate 	for (i = 0; i <= n; i++)
1837c478bd9Sstevel@tonic-gate 		if (BN_is_bit_set(dh_pub, i))
1847c478bd9Sstevel@tonic-gate 			bits_set++;
1857c478bd9Sstevel@tonic-gate 	debug("bits set: %d/%d", bits_set, BN_num_bits(dh->p));
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	/* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */
1887c478bd9Sstevel@tonic-gate 	if (bits_set > 1 && (BN_cmp(dh_pub, dh->p) == -1))
1897c478bd9Sstevel@tonic-gate 		return 1;
1907c478bd9Sstevel@tonic-gate 	log("invalid public DH value (%d/%d)", bits_set, BN_num_bits(dh->p));
1917c478bd9Sstevel@tonic-gate 	return 0;
1927c478bd9Sstevel@tonic-gate }
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate void
dh_gen_key(DH * dh,int need)1957c478bd9Sstevel@tonic-gate dh_gen_key(DH *dh, int need)
1967c478bd9Sstevel@tonic-gate {
1977c478bd9Sstevel@tonic-gate 	int i, bits_set = 0, tries = 0;
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	if (dh->p == NULL)
2007c478bd9Sstevel@tonic-gate 		fatal("dh_gen_key: dh->p == NULL");
2017c478bd9Sstevel@tonic-gate 	if (2*need >= BN_num_bits(dh->p))
2027c478bd9Sstevel@tonic-gate 		fatal("dh_gen_key: group too small: %d (2*need %d)",
2037c478bd9Sstevel@tonic-gate 		    BN_num_bits(dh->p), 2*need);
2047c478bd9Sstevel@tonic-gate 	do {
2057c478bd9Sstevel@tonic-gate 		if (dh->priv_key != NULL)
2067c478bd9Sstevel@tonic-gate 			BN_clear_free(dh->priv_key);
2077c478bd9Sstevel@tonic-gate 		if ((dh->priv_key = BN_new()) == NULL)
2087c478bd9Sstevel@tonic-gate 			fatal("dh_gen_key: BN_new failed");
2097c478bd9Sstevel@tonic-gate 		/* generate a 2*need bits random private exponent */
2107c478bd9Sstevel@tonic-gate 		if (!BN_rand(dh->priv_key, 2*need, 0, 0))
2117c478bd9Sstevel@tonic-gate 			fatal("dh_gen_key: BN_rand failed");
2127c478bd9Sstevel@tonic-gate 		if (DH_generate_key(dh) == 0)
213*cd7d5fafSJan Pechanec 			fatal("dh_gen_key: DH_generate_key() failed");
2147c478bd9Sstevel@tonic-gate 		for (i = 0; i <= BN_num_bits(dh->priv_key); i++)
2157c478bd9Sstevel@tonic-gate 			if (BN_is_bit_set(dh->priv_key, i))
2167c478bd9Sstevel@tonic-gate 				bits_set++;
2177c478bd9Sstevel@tonic-gate 		debug("dh_gen_key: priv key bits set: %d/%d",
2187c478bd9Sstevel@tonic-gate 		    bits_set, BN_num_bits(dh->priv_key));
2197c478bd9Sstevel@tonic-gate 		if (tries++ > 10)
2207c478bd9Sstevel@tonic-gate 			fatal("dh_gen_key: too many bad keys: giving up");
2217c478bd9Sstevel@tonic-gate 	} while (!dh_pub_is_valid(dh, dh->pub_key));
2227c478bd9Sstevel@tonic-gate }
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate DH *
dh_new_group_asc(const char * gen,const char * modulus)2257c478bd9Sstevel@tonic-gate dh_new_group_asc(const char *gen, const char *modulus)
2267c478bd9Sstevel@tonic-gate {
2277c478bd9Sstevel@tonic-gate 	DH *dh;
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	if ((dh = DH_new()) == NULL)
2307c478bd9Sstevel@tonic-gate 		fatal("dh_new_group_asc: DH_new");
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 	if (BN_hex2bn(&dh->p, modulus) == 0)
2337c478bd9Sstevel@tonic-gate 		fatal("BN_hex2bn p");
2347c478bd9Sstevel@tonic-gate 	if (BN_hex2bn(&dh->g, gen) == 0)
2357c478bd9Sstevel@tonic-gate 		fatal("BN_hex2bn g");
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	return (dh);
2387c478bd9Sstevel@tonic-gate }
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate /*
2417c478bd9Sstevel@tonic-gate  * This just returns the group, we still need to generate the exchange
2427c478bd9Sstevel@tonic-gate  * value.
2437c478bd9Sstevel@tonic-gate  */
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate DH *
dh_new_group(BIGNUM * gen,BIGNUM * modulus)2467c478bd9Sstevel@tonic-gate dh_new_group(BIGNUM *gen, BIGNUM *modulus)
2477c478bd9Sstevel@tonic-gate {
2487c478bd9Sstevel@tonic-gate 	DH *dh;
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	if ((dh = DH_new()) == NULL)
2517c478bd9Sstevel@tonic-gate 		fatal("dh_new_group: DH_new");
2527c478bd9Sstevel@tonic-gate 	dh->p = modulus;
2537c478bd9Sstevel@tonic-gate 	dh->g = gen;
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	return (dh);
2567c478bd9Sstevel@tonic-gate }
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate DH *
dh_new_group1(void)2597c478bd9Sstevel@tonic-gate dh_new_group1(void)
2607c478bd9Sstevel@tonic-gate {
2617c478bd9Sstevel@tonic-gate 	static char *gen = "2", *group1 =
2627c478bd9Sstevel@tonic-gate 	    "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2637c478bd9Sstevel@tonic-gate 	    "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2647c478bd9Sstevel@tonic-gate 	    "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2657c478bd9Sstevel@tonic-gate 	    "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2667c478bd9Sstevel@tonic-gate 	    "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2677c478bd9Sstevel@tonic-gate 	    "FFFFFFFF" "FFFFFFFF";
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	return (dh_new_group_asc(gen, group1));
2707c478bd9Sstevel@tonic-gate }
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate /*
2737c478bd9Sstevel@tonic-gate  * Estimates the group order for a Diffie-Hellman group that has an
2747c478bd9Sstevel@tonic-gate  * attack complexity approximately the same as O(2**bits).  Estimate
2757c478bd9Sstevel@tonic-gate  * with:  O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3)))
2767c478bd9Sstevel@tonic-gate  */
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate int
dh_estimate(int bits)2797c478bd9Sstevel@tonic-gate dh_estimate(int bits)
2807c478bd9Sstevel@tonic-gate {
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	if (bits < 64)
2837c478bd9Sstevel@tonic-gate 		return (512);	/* O(2**63) */
2847c478bd9Sstevel@tonic-gate 	if (bits < 128)
2857c478bd9Sstevel@tonic-gate 		return (1024);	/* O(2**86) */
2867c478bd9Sstevel@tonic-gate 	if (bits < 192)
2877c478bd9Sstevel@tonic-gate 		return (2048);	/* O(2**116) */
2887c478bd9Sstevel@tonic-gate 	return (4096);		/* O(2**156) */
2897c478bd9Sstevel@tonic-gate }
290