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