From cd60a602f541ac44aecd207f4e055279b86d1898 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 28 Nov 2021 12:10:42 +0000 Subject: [PATCH] Stop using short exponents for Diffie-Hellman. I recently encountered a paper [1] which catalogues all kinds of things that can go wrong when one party in a discrete-log system invents a prime and the other party chooses an exponent. In particular, some choices of prime make it reasonable to use a short exponent to save time, but others make that strategy very bad. That paper is about the ElGamal encryption scheme used in OpenPGP, which is basically integer Diffie-Hellman with one side's key being persistent: a shared-secret integer is derived exactly as in DH, and then it's used to communicate a message integer by simply multiplying the shared secret by the message, mod p. I don't _know_ that any problem of this kind arises in the SSH usage of Diffie-Hellman: the standard integer DH groups in SSH are safe primes, and as far as I know, the usual generation of prime moduli for DH group exchange also picks safe primes. So the short exponents PuTTY has been using _should_ be OK. However, the range of imaginative other possibilities shown in that paper make me nervous, even so! So I think I'm going to retire the short exponent strategy, on general principles of overcaution. This slows down 4096-bit integer DH by about a factor of 3-4 (which would be worse if it weren't for the modpow speedup in the previous commit). I think that's OK, because, firstly, computers are a lot faster these days than when I originally chose to use short exponents, and secondly, more and more implementations are now switching to elliptic-curve DH, which is unaffected by this change (and with which we've always been using maximum-length exponents). [1] On the (in)security of ElGamal in OpenPGP. Luca De Feo, Bertram Poettering, Alessandro Sorniotti. https://eprint.iacr.org/2021/923 --- crypto/diffie-hellman.c | 19 +------------------ ssh.h | 2 +- ssh/kex2-client.c | 4 ++-- ssh/kex2-server.c | 2 +- test/testcrypt-func.h | 2 +- 5 files changed, 6 insertions(+), 23 deletions(-) diff --git a/crypto/diffie-hellman.c b/crypto/diffie-hellman.c index a1ed7cb4..914167bb 100644 --- a/crypto/diffie-hellman.c +++ b/crypto/diffie-hellman.c @@ -200,19 +200,8 @@ void dh_cleanup(dh_ctx *ctx) /* * DH stage 1: invent a number x between 1 and q, and compute e = * g^x mod p. Return e. - * - * If `nbits' is greater than zero, it is used as an upper limit - * for the number of bits in x. This is safe provided that (a) you - * use twice as many bits in x as the number of bits you expect to - * use in your session key, and (b) the DH group is a safe prime - * (which SSH demands that it must be). - * - * P. C. van Oorschot, M. J. Wiener - * "On Diffie-Hellman Key Agreement with Short Exponents". - * Advances in Cryptology: Proceedings of Eurocrypt '96 - * Springer-Verlag, May 1996. */ -mp_int *dh_create_e(dh_ctx *ctx, int nbits) +mp_int *dh_create_e(dh_ctx *ctx) { /* * Lower limit is just 2. @@ -224,12 +213,6 @@ mp_int *dh_create_e(dh_ctx *ctx, int nbits) */ mp_int *hi = mp_copy(ctx->q); mp_sub_integer_into(hi, hi, 1); - if (nbits) { - mp_int *pow2 = mp_power_2(nbits+1); - mp_min_into(pow2, pow2, hi); - mp_free(hi); - hi = pow2; - } /* * Make a random number in that range. diff --git a/ssh.h b/ssh.h index 7049f2af..77cddf99 100644 --- a/ssh.h +++ b/ssh.h @@ -1223,7 +1223,7 @@ dh_ctx *dh_setup_group(const ssh_kex *kex); dh_ctx *dh_setup_gex(mp_int *pval, mp_int *gval); int dh_modulus_bit_size(const dh_ctx *ctx); void dh_cleanup(dh_ctx *); -mp_int *dh_create_e(dh_ctx *, int nbits); +mp_int *dh_create_e(dh_ctx *); const char *dh_validate_f(dh_ctx *, mp_int *f); mp_int *dh_find_K(dh_ctx *, mp_int *f); diff --git a/ssh/kex2-client.c b/ssh/kex2-client.c index 8a3a290a..0f81ef15 100644 --- a/ssh/kex2-client.c +++ b/ssh/kex2-client.c @@ -118,7 +118,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) * Now generate and send e for Diffie-Hellman. */ seat_set_busy_status(s->ppl.seat, BUSY_CPU); - s->e = dh_create_e(s->dh_ctx, s->nbits * 2); + s->e = dh_create_e(s->dh_ctx); pktout = ssh_bpp_new_pktout(s->ppl.bpp, s->kex_init_value); put_mp_ssh2(pktout, s->e); pq_push(s->ppl.out_pq, pktout); @@ -322,7 +322,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) "exchange with hash %s", ssh_hash_alg(s->exhash)->text_name); /* Now generate e for Diffie-Hellman. */ seat_set_busy_status(s->ppl.seat, BUSY_CPU); - s->e = dh_create_e(s->dh_ctx, s->nbits * 2); + s->e = dh_create_e(s->dh_ctx); if (s->shgss->lib->gsslogmsg) ppl_logevent("%s", s->shgss->lib->gsslogmsg); diff --git a/ssh/kex2-server.c b/ssh/kex2-server.c index 56bdc3c5..3c017077 100644 --- a/ssh/kex2-server.c +++ b/ssh/kex2-server.c @@ -128,7 +128,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) /* * Generate e for Diffie-Hellman. */ - s->e = dh_create_e(s->dh_ctx, s->nbits * 2); + s->e = dh_create_e(s->dh_ctx); /* * Wait to receive f. diff --git a/test/testcrypt-func.h b/test/testcrypt-func.h index 9158e800..b8e53a96 100644 --- a/test/testcrypt-func.h +++ b/test/testcrypt-func.h @@ -339,7 +339,7 @@ FUNC_WRAPPED(val_string, ssh_cipher_decrypt_length, ARG(val_cipher, c), FUNC(val_dh, dh_setup_group, ARG(dh_group, group)) FUNC(val_dh, dh_setup_gex, ARG(val_mpint, p), ARG(val_mpint, g)) FUNC(uint, dh_modulus_bit_size, ARG(val_dh, ctx)) -FUNC(val_mpint, dh_create_e, ARG(val_dh, ctx), ARG(uint, nbits)) +FUNC(val_mpint, dh_create_e, ARG(val_dh, ctx)) FUNC_WRAPPED(boolean, dh_validate_f, ARG(val_dh, ctx), ARG(val_mpint, f)) FUNC(val_mpint, dh_find_K, ARG(val_dh, ctx), ARG(val_mpint, f))