Skip to content

Commit 9f912f1

Browse files
author
Legrandin
committed
Fix to bug #985164 (ElGamal key generation). Fix to missing range check in signature verification.
1 parent c8e2138 commit 9f912f1

File tree

1 file changed

+40
-24
lines changed

1 file changed

+40
-24
lines changed

Diff for: lib/Crypto/PublicKey/ElGamal.py

+40-24
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ class error (Exception):
110110
def generate(bits, randfunc, progress_func=None):
111111
"""Randomly generate a fresh, new ElGamal key.
112112
113+
The key will be safe for use for both encryption and signature
114+
(although it should be used for **only one** purpose).
115+
113116
:Parameters:
114117
bits : int
115118
Key length, or size (in bits) of the modulus *p*.
@@ -131,37 +134,48 @@ def generate(bits, randfunc, progress_func=None):
131134
:Return: An ElGamal key object (`ElGamalobj`).
132135
"""
133136
obj=ElGamalobj()
134-
# Generate prime p
137+
# Generate a safe prime p
138+
# See Algorithm 4.86 in Handbook of Applied Cryptography
135139
if progress_func:
136140
progress_func('p\n')
137-
obj.p=bignum(getPrime(bits, randfunc))
138-
# Generate random number g
141+
while 1:
142+
q = bignum(getPrime(bits-1, randfunc))
143+
obj.p = 2*q+1
144+
if number.isPrime(obj.p, randfunc=randfunc):
145+
break
146+
# Generate generator g
147+
# See Algorithm 4.80 in Handbook of Applied Cryptography
148+
# Note that the order of the group is n=p-1=2q, where q is prime
139149
if progress_func:
140150
progress_func('g\n')
141-
size=bits-1-(ord(randfunc(1)) & 63) # g will be from 1--64 bits smaller than p
142-
if size<1:
143-
size=bits-1
144-
while (1):
145-
obj.g=bignum(getPrime(size, randfunc))
146-
if obj.g < obj.p:
151+
while 1:
152+
# We must avoid g=2 because of Bleichenbacher's attack described
153+
# in "Generating ElGamal signatures without knowning the secret key",
154+
# 1996
155+
#
156+
obj.g = number.getRandomRange(3, obj.p, randfunc)
157+
safe = 1
158+
if pow(obj.g, 2, obj.p)==1:
159+
safe=0
160+
if safe and pow(obj.g, q, obj.p)==1:
161+
safe=0
162+
# Discard g if it divides p-1 because of the attack described
163+
# in Note 11.67 (iii) in HAC
164+
if safe and divmod(obj.p-1, obj.g)[1]==0:
165+
safe=0
166+
# g^{-1} must not divide p-1 because of Khadir's attack
167+
# described in "Conditions of the generator for forging ElGamal
168+
# signature", 2011
169+
ginv = number.inverse(obj.g, obj.p)
170+
if safe and divmod(obj.p-1, ginv)[1]==0:
171+
safe=0
172+
if safe:
147173
break
148-
size=(size+1) % bits
149-
if size==0:
150-
size=4
151-
# Generate random number x
174+
# Generate private key x
152175
if progress_func:
153176
progress_func('x\n')
154-
while (1):
155-
size=bits-1-ord(randfunc(1)) # x will be from 1 to 256 bits smaller than p
156-
if size>2:
157-
break
158-
while (1):
159-
obj.x=bignum(getPrime(size, randfunc))
160-
if obj.x < obj.p:
161-
break
162-
size = (size+1) % bits
163-
if size==0:
164-
size=4
177+
obj.x=number.getRandomRange(2, obj.p-1, randfunc)
178+
# Generate public key y
165179
if progress_func:
166180
progress_func('y\n')
167181
obj.y = pow(obj.g, obj.x, obj.p)
@@ -326,6 +340,8 @@ def _sign(self, M, K):
326340
return (a, b)
327341

328342
def _verify(self, M, sig):
343+
if sig[0]<1 or sig[0]>p-1:
344+
return 0
329345
v1=pow(self.y, sig[0], self.p)
330346
v1=(v1*pow(sig[0], sig[1], self.p)) % self.p
331347
v2=pow(self.g, M, self.p)

0 commit comments

Comments
 (0)