1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
Description: Fix insecure ElGamal key generation.
Author: Legrandin <gooksankoo@hoiptorrow.mailexpire.com>
Origin:
https://github.com/dlitz/pycrypto/commit/9f912f13,
https://github.com/dlitz/pycrypto/commit/c575de4f
Last-Update: 2012-05-25
Index: python-crypto-2.1.0/lib/Crypto/PublicKey/ElGamal.py
===================================================================
--- python-crypto-2.1.0.orig/lib/Crypto/PublicKey/ElGamal.py 2012-05-25 01:25:55.000000000 +0200
+++ python-crypto-2.1.0/lib/Crypto/PublicKey/ElGamal.py 2012-05-25 14:59:35.000000000 +0200
@@ -40,37 +40,48 @@
the progress of the key generation.
"""
obj=ElGamalobj()
- # Generate prime p
+ # Generate a safe prime p
+ # See Algorithm 4.86 in Handbook of Applied Cryptography
if progress_func:
progress_func('p\n')
- obj.p=bignum(getPrime(bits, randfunc))
- # Generate random number g
+ while 1:
+ q = bignum(getPrime(bits-1, randfunc))
+ obj.p = 2*q+1
+ 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')
- size=bits-1-(ord(randfunc(1)) & 63) # g will be from 1--64 bits smaller than p
- if size<1:
- size=bits-1
- while (1):
- obj.g=bignum(getPrime(size, randfunc))
- if obj.g < obj.p:
+ while 1:
+ # 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
+ # 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
+ # 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
- size=(size+1) % bits
- if size==0:
- size=4
- # Generate random number x
+ # Generate private key x
if progress_func:
progress_func('x\n')
- while (1):
- size=bits-1-ord(randfunc(1)) # x will be from 1 to 256 bits smaller than p
- if size>2:
- break
- while (1):
- obj.x=bignum(getPrime(size, randfunc))
- if obj.x < obj.p:
- break
- size = (size+1) % bits
- if size==0:
- size=4
+ obj.x=number.getRandomRange(2, obj.p-1, randfunc)
+ # Generate public key y
if progress_func:
progress_func('y\n')
obj.y = pow(obj.g, obj.x, obj.p)
@@ -118,6 +129,8 @@
return (a, b)
def _verify(self, M, sig):
+ if sig[0]<1 or sig[0]>self.p-1:
+ return 0
v1=pow(self.y, sig[0], self.p)
v1=(v1*pow(sig[0], sig[1], self.p)) % self.p
v2=pow(self.g, M, self.p)
Index: python-crypto-2.1.0/lib/Crypto/Util/number.py
===================================================================
--- python-crypto-2.1.0.orig/lib/Crypto/Util/number.py 2012-05-25 01:25:55.000000000 +0200
+++ python-crypto-2.1.0/lib/Crypto/Util/number.py 2012-05-25 01:25:57.000000000 +0200
@@ -84,6 +84,22 @@
assert size(value) >= N
return value
+def getRandomRange(a, b, randfunc=None):
+ """getRandomRange(a:int, b:int, randfunc:callable):long
+ Return a random number n so that a <= n < b.
+
+ If randfunc is omitted, then Random.new().read is used.
+
+ This function is for internal use only and may be renamed or removed in
+ the future.
+ """
+ range_ = b - a - 1
+ bits = size(range_)
+ value = getRandomNumber(bits, randfunc)
+ while value > range_:
+ value = getRandomNumber(bits, randfunc)
+ return a + value
+
def GCD(x,y):
"""GCD(x:long, y:long): long
Return the GCD of x and y.
|