From 2f6c124e127b5dd98723e7e75a9825c4ed8bd5c7 Mon Sep 17 00:00:00 2001 From: Paul Howarth Date: Fri, 23 Feb 2018 13:03:13 +0000 Subject: [PATCH] Backport of fix for CVE-2018-6594 from pycryptodome When creating ElGamal keys, the generator wasn't a square residue: ElGamal encryption done with those keys cannot be secure under the DDH assumption. More details: - https://github.com/TElgamal/attack-on-pycrypto-elgamal - https://github.com/Legrandin/pycryptodome/issues/90 - https://github.com/dlitz/pycrypto/issues/253 This commit is a backport to pycrypto of Legrandin/pycryptodome@99c27a3b Thanks to Weikeng Chen. --- lib/Crypto/PublicKey/ElGamal.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/Crypto/PublicKey/ElGamal.py b/lib/Crypto/PublicKey/ElGamal.py index 0ab07fc8..064e42bf 100644 --- a/lib/Crypto/PublicKey/ElGamal.py +++ b/lib/Crypto/PublicKey/ElGamal.py @@ -154,33 +154,33 @@ def generate(bits, randfunc, progress_func=None): if number.isPrime(obj.p, randfunc=randfunc): break # Generate generator g - # See Algorithm 4.80 in Handbook of Applied Cryptography - # Note that the order of the group is n=p-1=2q, where q is prime if progress_func: progress_func('g\n') while 1: + # Choose a square residue; it will generate a cyclic group of order q. + obj.g = pow(number.getRandomRange(2, obj.p, randfunc), 2, obj.p) + # We must avoid g=2 because of Bleichenbacher's attack described # in "Generating ElGamal signatures without knowning the secret key", # 1996 - # - obj.g = number.getRandomRange(3, obj.p, randfunc) - safe = 1 - if pow(obj.g, 2, obj.p)==1: - safe=0 - if safe and pow(obj.g, q, obj.p)==1: - safe=0 + if obj.g in (1, 2): + continue + # Discard g if it divides p-1 because of the attack described # in Note 11.67 (iii) in HAC - if safe and divmod(obj.p-1, obj.g)[1]==0: - safe=0 + if (obj.p - 1) % obj.g == 0: + continue + # g^{-1} must not divide p-1 because of Khadir's attack # described in "Conditions of the generator for forging ElGamal # signature", 2011 ginv = number.inverse(obj.g, obj.p) - if safe and divmod(obj.p-1, ginv)[1]==0: - safe=0 - if safe: - break + if (obj.p - 1) % ginv == 0: + continue + + # Found + break + # Generate private key x if progress_func: progress_func('x\n')