def _decrypt(self, ciphertext): if not 0 < ciphertext < self._n: raise ValueError("Ciphertext too large") if not self.has_private(): raise TypeError("This is not a private key") # Blinded RSA decryption (to prevent timing attacks): # Step 1: Generate random secret blinding factor r, # such that 0 < r < n-1 r = Integer.random_range(min_inclusive=1, max_exclusive=self._n) # Step 2: Compute c' = c * r**e mod n cp = Integer(ciphertext) * pow(r, self._e, self._n) % self._n # Step 3: Compute m' = c'**d mod n (ordinary RSA decryption) m1 = pow(cp, self._d % (self._p - 1), self._p) m2 = pow(cp, self._d % (self._q - 1), self._q) h = m2 - m1 while h < 0: h += self._q h = (h * self._u) % self._q mp = h * self._p + m1 # Step 4: Compute m = m**(r-1) mod n result = (r.inverse(self._n) * mp) % self._n # Verify no faults occured if ciphertext != pow(result, self._e, self._n): raise ValueError("Fault detected in RSA decryption") return result
def sign(elg, m): h = int(SHA1.new(m).hexdigest(), 16) q = (elg.p - 1) // 2 k = Integer.random_range(min_inclusive=1, max_exclusive=2**16) r = pow(elg.g, k, elg.p) % q s = Integer(k).inverse(q) * (Integer(h) + elg.x * r) % q return (int(r), int(s)), int(k)
def _decrypt(self, ciphertext): if not 0 < ciphertext < self._n: raise ValueError("Ciphertext too large") if not self.has_private(): raise TypeError("This is not a private key") # Blinded RSA decryption (to prevent timing attacks): # Step 1: Generate random secret blinding factor r, # such that 0 < r < n-1 r = Integer.random_range(min_inclusive=1, max_exclusive=self._n) # Step 2: Compute c' = c * r**e mod n cp = Integer(ciphertext) * pow(r, self._e, self._n) % self._n # Step 3: Compute m' = c'**d mod n (ordinary RSA decryption) m1 = pow(cp, self._d % (self._p - 1), self._p) m2 = pow(cp, self._d % (self._q - 1), self._q) h = m2 - m1 while h < 0: h += self._q h = (h * self._u) % self._q mp = h * self._p + m1 # Step 4: Compute m = m**(r-1) mod n result = (r.inverse(self._n) * mp) % self._n # Verify no faults occured if ciphertext != pow(result, self._e, self._n): raise ValueError("Fault detected in RSA decryption") return result
def _decrypt(self, ciphertext): if not 0 < ciphertext < self.n: raise ValueError("Ciphertext too large") if not self.has_private(): raise TypeError("This is not a private key") e, d, n, p, q, u = [self._key[comp] for comp in 'e', 'd', 'n', 'p', 'q', 'u'] # Blinded RSA decryption (to prevent timing attacks): # Step 1: Generate random secret blinding factor r, such that 0 < r < n-1 r = Integer.random_range(min_inclusive=1, max_exclusive=n) # Step 2: Compute c' = c * r**e mod n cp = Integer(ciphertext) * pow(r, e, n) % n # Step 3: Compute m' = c'**d mod n (ordinary RSA decryption) m1 = pow(cp, d % (p - 1), p) m2 = pow(cp, d % (q - 1), q) h = m2 - m1 while h < 0: h += q h = (h * u) % q mp = h * p + m1 # Step 4: Compute m = m**(r-1) mod n result = (r.inverse(n) * mp) % n # Verify no faults occured if ciphertext != pow(result, e, n): raise ValueError("Fault detected in RSA decryption") return result
def find_generator(p): # Just finds a group generator; not part of the challenge # From https://github.com/Legrandin/pycryptodome/blob/6fb4ca4c73d7e80f336c183dd8ed906d3c4320a2/lib/Crypto/PublicKey/ElGamal.py#L60 p = Integer(p) while 1: # Choose a square residue; it will generate a cyclic group of order q. g = pow(Integer.random_range(min_inclusive=2, max_exclusive=p), 2, p) # We must avoid g=2 because of Bleichenbacher's attack described # in "Generating ElGamal signatures without knowning the secret key", # 1996 if 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 (p - 1) % 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 = g.inverse(p) if (p - 1) % ginv == 0: continue # Found break return int(g)
def _decrypt(self, ciphertext): if not 0 < ciphertext < self.n: raise ValueError("Ciphertext too large") if not self.has_private(): raise TypeError("This is not a private key") e, d, n, p, q, u = [ self._key[comp] for comp in 'e', 'd', 'n', 'p', 'q', 'u' ] # Blinded RSA decryption (to prevent timing attacks): # Step 1: Generate random secret blinding factor r, such that 0 < r < n-1 r = Integer.random_range(min_inclusive=1, max_exclusive=n) # Step 2: Compute c' = c * r**e mod n cp = Integer(ciphertext) * pow(r, e, n) % n # Step 3: Compute m' = c'**d mod n (ordinary RSA decryption) m1 = pow(cp, d % (p - 1), p) m2 = pow(cp, d % (q - 1), q) h = m2 - m1 while h < 0: h += q h = (h * u) % q mp = h * p + m1 # Step 4: Compute m = m**(r-1) mod n result = (r.inverse(n) * mp) % n # Verify no faults occured if ciphertext != pow(result, e, n): raise ValueError("Fault detected in RSA decryption") return result
def _decrypt(self, M): if (not hasattr(self, 'x')): raise TypeError('Private key not available in this object') r = Integer.random_range(min_inclusive=2, max_exclusive=self.p-1, randfunc=self._randfunc) a_blind = (pow(self.g, r, self.p) * M[0]) % self.p ax=pow(a_blind, self.x, self.p) plaintext_blind = (ax.inverse(self.p) * M[1] ) % self.p plaintext = (plaintext_blind * pow(self.y, r, self.p)) % self.p return int(plaintext)
def _decrypt(self, M): if (not hasattr(self, 'x')): raise TypeError('Private key not available in this object') r = Integer.random_range(min_inclusive=2, max_exclusive=self.p - 1, randfunc=self._randfunc) a_blind = (pow(self.g, r, self.p) * M[0]) % self.p ax = pow(a_blind, self.x, self.p) plaintext_blind = (ax.inverse(self.p) * M[1]) % self.p plaintext = (plaintext_blind * pow(self.y, r, self.p)) % self.p return int(plaintext)
def _sign(self, z, k): assert 0 < k < _curve.order blind = Integer.random_range(min_inclusive=1, max_exclusive=_curve.order) blind_d = self._d * blind inv_blind_k = (blind * k).inverse(_curve.order) r = (_curve.G * k).x % _curve.order s = inv_blind_k * (blind * z + blind_d * r) % _curve.order return (r, s)
def _sign(self, z, k): assert 0 < k < self._curve.order order = self._curve.order blind = Integer.random_range(min_inclusive=1, max_exclusive=order) blind_d = self._d * blind inv_blind_k = (blind * k).inverse(order) r = (self._curve.G * k).x % order s = inv_blind_k * (blind * z + blind_d * r) % order return (r, s)
def _sign(self, m, k): if not self.has_private(): raise TypeError("DSA public key cannot be used for signing") if not (1 < k < self.q): raise ValueError("k is not between 2 and q-1") x, q, p, g = [self._key[comp] for comp in ['x', 'q', 'p', 'g']] blind_factor = Integer.random_range(min_inclusive=1, max_exclusive=q) inv_blind_k = (blind_factor * k).inverse(q) blind_x = x * blind_factor r = pow(g, k, p) % q # r = (g**k mod p) mod q s = (inv_blind_k * (blind_factor * m + blind_x * r)) % q return list(map(int, (r, s)))
def _sign(self, m, k): if not self.has_private(): raise TypeError("DSA public key cannot be used for signing") if not (1 < k < self.q): raise ValueError("k is not between 2 and q-1") x, q, p, g = [self._key[comp] for comp in ['x', 'q', 'p', 'g']] blind_factor = Integer.random_range(min_inclusive=1, max_exclusive=q) inv_blind_k = (blind_factor * k).inverse(q) blind_x = x * blind_factor r = pow(g, k, p) % q # r = (g**k mod p) mod q s = (inv_blind_k * (blind_factor * m + blind_x * r)) % q return map(int, (r, s))
def run_test_scalarmul_random(): randkey = ECC.generate(curve='P-256') randx = int(randkey.public_key().pointQ.x.to_bytes(32).hex(), 16) randy = int(randkey.public_key().pointQ.y.to_bytes(32).hex(), 16) randk = int( Integer.random_range( min_inclusive=1, max_exclusive=P256_CURVE_ORDER).to_bytes(32).hex(), 16) randp = ECC.EccPoint(randx, randy, curve='p256') resref = randp * randk init_dmem() xres, yres = run_scalarmult(randx, randy, randk) resbn = ECC.EccPoint(xres, yres, curve='p256') if not resref == resbn: raise Exception( 'Wrong result for scalar point multiplication (random)')
def run_test_ecdsa_sign_random(): init_dmem() randkey = ECC.generate(curve='P-256') randd = int(randkey.d.to_bytes(32).hex(), 16) randk = int( Integer.random_range( min_inclusive=1, max_exclusive=P256_CURVE_ORDER).to_bytes(32).hex(), 16) rres, sres = run_sign(randd, randk, msg_digest_int) rresb = rres.to_bytes(32, byteorder='big', signed=False) sresb = sres.to_bytes(32, byteorder='big', signed=False) rsresb = b''.join([rresb, sresb]) verifier = DSS.new(randkey, 'fips-186-3') try: verifier.verify(msg_digest, rsresb) except ValueError: raise Exception('ECDSA sign (random) failed')
def generate(**kwargs): """Generate a new private key on the given curve. :Keywords: curve : string Mandatory. It must be "P-256", "prime256v1" or "secp256r1". randfunc : callable Optional. The RNG to read randomness from. If ``None``, the system source is used. """ curve = kwargs.pop("curve") randfunc = kwargs.pop("randfunc", get_random_bytes) if kwargs: raise TypeError("Unknown parameters: " + str(kwargs)) d = Integer.random_range(min_inclusive=1, max_exclusive=_curve.order, randfunc=randfunc) return EccKey(curve=curve, d=d)
def generate(**kwargs): """Generate a new private key on the given curve. :Keywords: curve : string Mandatory. It must be "P-256", "prime256v1" or "secp256r1". randfunc : callable Optional. The RNG to read randomness from. If ``None``, the system source is used. """ curve = kwargs.pop("curve") randfunc = kwargs.pop("randfunc", get_random_bytes) if kwargs: raise TypeError("Unknown parameters: " + str(kwargs)) d = Integer.random_range(min_inclusive=1, max_exclusive=_curve.order, randfunc=randfunc) return EccKey(curve=curve, d=d)
def generate(**kwargs): """Generate a new private key on the given curve. Args: curve (string): Mandatory. It must be a curve name defined in :numref:`curve_names`. randfunc (callable): Optional. The RNG to read randomness from. If ``None``, :func:`Crypto.Random.get_random_bytes` is used. """ curve_name = kwargs.pop("curve") curve = _curves[curve_name] randfunc = kwargs.pop("randfunc", get_random_bytes) if kwargs: raise TypeError("Unknown parameters: " + str(kwargs)) d = Integer.random_range(min_inclusive=1, max_exclusive=curve.order, randfunc=randfunc) return EccKey(curve=curve_name, d=d)
def generate(**kwargs): """Generate a new private key on the given curve. Args: curve (string): Mandatory. It must be a curve name defined in :numref:`curve_names`. randfunc (callable): Optional. The RNG to read randomness from. If ``None``, :func:`Crypto.Random.get_random_bytes` is used. """ curve_name = kwargs.pop("curve") curve = _curves[curve_name] randfunc = kwargs.pop("randfunc", get_random_bytes) if kwargs: raise TypeError("Unknown parameters: " + str(kwargs)) d = Integer.random_range(min_inclusive=1, max_exclusive=curve.order, randfunc=randfunc) return EccKey(curve=curve_name, d=d)
def _compute_nonce(self, msg_hash): return Integer.random_range(min_inclusive=1, max_exclusive=_curve.order, randfunc=self._randfunc)
def _compute_nonce(self, msg_hash): # hash is not used return Integer.random_range(min_inclusive=1, max_exclusive=self._order, randfunc=self._randfunc)
def main(): global inst_cnt global cycle_cnt global ctx """main""" init_dmem() load_program() # curve point test (deterministic) inst_cnt = 0 cycle_cnt = 0 res = run_isoncurve(xexp, yexp) if not res: raise Exception('Test point (deterministic) should be on curve') ins_point_test = inst_cnt cyc_point_test = cycle_cnt # curve point test (random) #rand = Integer.random_range(min_inclusive=1, max_exclusive=P256_CURVE_ORDER) randkey = ECC.generate(curve='P-256') randx = int(randkey.public_key().pointQ.x.to_bytes(32).hex(), 16) randy = int(randkey.public_key().pointQ.y.to_bytes(32).hex(), 16) inst_cnt = 0 cycle_cnt = 0 res = run_isoncurve(randx, randy) if not res: raise Exception('Test point (deterministic) should be on curve') ins_point_test_rnd = inst_cnt cyc_point_test_rnd = cycle_cnt # scalar multiplication (deterministic) inst_cnt = 0 cycle_cnt = 0 pointexp = ECC.EccPoint(xexp, yexp, curve='p256') resref = pointexp*kexp init_dmem() xres, yres = run_scalarmult(xexp, yexp, kexp) resbn = ECC.EccPoint(xres, yres, curve='p256') if not resref == resbn: raise Exception('Wrong result for scalar point multiplication (deterministic)') ins_scalar_mult = inst_cnt cyc_scalar_mult = cycle_cnt # scalar multiplication (random) inst_cnt = 0 cycle_cnt = 0 randkey = ECC.generate(curve='P-256') randx = int(randkey.public_key().pointQ.x.to_bytes(32).hex(), 16) randy = int(randkey.public_key().pointQ.y.to_bytes(32).hex(), 16) randk = int(Integer.random_range(min_inclusive=1, max_exclusive=P256_CURVE_ORDER).to_bytes(32).hex(), 16) randp = ECC.EccPoint(randx, randy, curve='p256') resref = randp*randk init_dmem() xres, yres = run_scalarmult(randx,randy, randk) resbn = ECC.EccPoint(xres, yres, curve='p256') if not resref == resbn: raise Exception('Wrong result for scalar point multiplication (deterministic)') ins_scalar_mult_rand = inst_cnt cyc_scalar_mult_rand = cycle_cnt # ECDSA sign (deterministic) inst_cnt = 0 cycle_cnt = 0 init_dmem() rres, sres = run_sign(d, kexp, msg_digest_int) rresb = rres.to_bytes(32, byteorder='big', signed=False) sresb = sres.to_bytes(32, byteorder='big', signed=False) rsresb = b''.join([rresb, sresb]) verkey =ECC.construct(curve='p256', point_x=x, point_y=y, d=d) verifier = DSS.new(verkey, 'fips-186-3') try: verifier.verify(msg_digest, rsresb) except ValueError: raise Exception('ECDSA sign (deterministic) failed') ins_sign = inst_cnt cyc_sign = cycle_cnt # ECDSA sign (random (random key, random k, deterministic message digest)) inst_cnt = 0 cycle_cnt = 0 init_dmem() randkey = ECC.generate(curve='P-256') randd = int(randkey.d.to_bytes(32).hex(), 16) randk = int(Integer.random_range(min_inclusive=1, max_exclusive=P256_CURVE_ORDER).to_bytes(32).hex(), 16) rres, sres = run_sign(randd, randk, msg_digest_int) rresb = rres.to_bytes(32, byteorder='big', signed=False) sresb = sres.to_bytes(32, byteorder='big', signed=False) rsresb = b''.join([rresb, sresb]) verifier = DSS.new(randkey, 'fips-186-3') try: verifier.verify(msg_digest, rsresb) except ValueError: raise Exception('ECDSA sign (random) failed') ins_sign_rand = inst_cnt cyc_sign_rand = cycle_cnt # ECDSA verify (deterministic) inst_cnt = 0 cycle_cnt = 0 init_dmem() res = run_verify(xexp, yexp, rexp, sexp, msg_digest_int) if not res: raise Exception('ECDSA verifiy (deterministic) failed') ins_verify = inst_cnt cyc_verify = cycle_cnt # ECDSA verify (random) inst_cnt = 0 cycle_cnt = 0 init_dmem() randkey = ECC.generate(curve='P-256') randx = int(randkey.public_key().pointQ.x.to_bytes(32).hex(), 16) randy = int(randkey.public_key().pointQ.y.to_bytes(32).hex(), 16) signer = DSS.new(randkey, 'fips-186-3') signature = signer.sign(msg_digest) r = int.from_bytes(signature[0:32], byteorder='big', signed=False) s = int.from_bytes(signature[32:64], byteorder='big', signed=False) res = run_verify(randx, randy, r, s, msg_digest_int) if not res: raise Exception('ECDSA verifiy (rand) failed') ins_verify_rand = inst_cnt cyc_verify_rand = cycle_cnt print('=== Instructions ===') print('point test (deterministic): ' + str(ins_point_test)) print('point test (random): ' + str(ins_point_test_rnd)) print('scalar multiplication (deterministic): ' + str(ins_scalar_mult)) print('scalar multiplication (random): ' + str(ins_scalar_mult_rand)) print('ECDSA sign(deterministic): ' + str(ins_sign)) print('ECDSA sign(random): ' + str(ins_sign_rand)) print('ECDSA verify(deterministic): ' + str(ins_verify)) print('ECDSA verify(random): ' + str(ins_verify_rand)) print('\n=== Cycles ===') print('point test (deterministic): ' + str(cyc_point_test)) print('point test (random): ' + str(cyc_point_test_rnd)) print('scalar multiplication (deterministic): ' + str(cyc_scalar_mult)) print('scalar multiplication (random): ' + str(cyc_scalar_mult_rand)) print('ECDSA sign(deterministic): ' + str(cyc_sign)) print('ECDSA sign(random): ' + str(cyc_sign_rand)) print('ECDSA verify(deterministic): ' + str(cyc_verify)) print('ECDSA verify(random): ' + str(cyc_verify_rand))
def generate(bits, randfunc): """Randomly generate a fresh, new ElGamal key. The key will be safe for use for both encryption and signature (although it should be used for **only one** purpose). Args: bits (int): Key length, or size (in bits) of the modulus *p*. The recommended value is 2048. randfunc (callable): Random number generation function; it should accept a single integer *N* and return a string of random *N* random bytes. Return: an :class:`ElGamalKey` object """ obj=ElGamalKey() # Generate a safe prime p # See Algorithm 4.86 in Handbook of Applied Cryptography obj.p = generate_probable_safe_prime(exact_bits=bits, randfunc=randfunc) q = (obj.p - 1) >> 1 # Generate generator g while 1: # Choose a square residue; it will generate a cyclic group of order q. obj.g = pow(Integer.random_range(min_inclusive=2, max_exclusive=obj.p, randfunc=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 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 (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 = obj.g.inverse(obj.p) if (obj.p - 1) % ginv == 0: continue # Found break # Generate private key x obj.x = Integer.random_range(min_inclusive=2, max_exclusive=obj.p-1, randfunc=randfunc) # Generate public key y obj.y = pow(obj.g, obj.x, obj.p) return obj
def miller_rabin_test(candidate, iterations, randfunc=None): """Perform a Miller-Rabin primality test on an integer. The test is specified in Section C.3.1 of `FIPS PUB 186-4`__. :Parameters: candidate : integer The number to test for primality. iterations : integer The maximum number of iterations to perform before declaring a candidate a probable prime. randfunc : callable An RNG function where bases are taken from. :Returns: ``Primality.COMPOSITE`` or ``Primality.PROBABLY_PRIME``. .. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf """ if not isinstance(candidate, Integer): candidate = Integer(candidate) if candidate in (1, 2, 3, 5): return PROBABLY_PRIME if candidate.is_even(): return COMPOSITE one = Integer(1) minus_one = Integer(candidate - 1) if randfunc is None: randfunc = Random.new().read # Step 1 and 2 m = Integer(minus_one) a = 0 while m.is_even(): m >>= 1 a += 1 # Skip step 3 # Step 4 for i in range(iterations): # Step 4.1-2 base = 1 while base in (one, minus_one): base = Integer.random_range(min_inclusive=2, max_inclusive=candidate - 2) assert(2 <= base <= candidate - 2) # Step 4.3-4.4 z = pow(base, m, candidate) if z in (one, minus_one): continue # Step 4.5 for j in range(1, a): z = pow(z, 2, candidate) if z == minus_one: break if z == one: return COMPOSITE else: return COMPOSITE # Step 5 return PROBABLY_PRIME
def miller_rabin_test(candidate, iterations, randfunc=None): """Perform a Miller-Rabin primality test on an integer. The test is specified in Section C.3.1 of `FIPS PUB 186-4`__. :Parameters: candidate : integer The number to test for primality. iterations : integer The maximum number of iterations to perform before declaring a candidate a probable prime. randfunc : callable An RNG function where bases are taken from. :Returns: ``Primality.COMPOSITE`` or ``Primality.PROBABLY_PRIME``. .. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf """ if not isinstance(candidate, Integer): candidate = Integer(candidate) if candidate in (1, 2, 3, 5): return PROBABLY_PRIME if candidate.is_even(): return COMPOSITE one = Integer(1) minus_one = Integer(candidate - 1) if randfunc is None: randfunc = Random.new().read # Step 1 and 2 m = Integer(minus_one) a = 0 while m.is_even(): m >>= 1 a += 1 # Skip step 3 # Step 4 for i in range(iterations): # Step 4.1-2 base = 1 while base in (one, minus_one): base = Integer.random_range(min_inclusive=2, max_inclusive=candidate - 2) assert(2 <= base <= candidate - 2) # Step 4.3-4.4 z = pow(base, m, candidate) if z in (one, minus_one): continue # Step 4.5 for j in range(1, a): z = pow(z, 2, candidate) if z == minus_one: break if z == one: return COMPOSITE else: return COMPOSITE # Step 5 return PROBABLY_PRIME
def _compute_nonce(self, msg_hash): return Integer.random_range(min_inclusive=1, max_exclusive=_curve.order, randfunc=self._randfunc)
def _compute_nonce(self, msg_hash): # hash is not used return Integer.random_range(min_inclusive=1, max_exclusive=self._order, randfunc=self._randfunc)
def generate(bits, randfunc): """Randomly generate a fresh, new ElGamal key. The key will be safe for use for both encryption and signature (although it should be used for **only one** purpose). :Parameters: bits : int Key length, or size (in bits) of the modulus *p*. Recommended value is 2048. randfunc : callable Random number generation function; it should accept a single integer N and return a string of random data N bytes long. :attention: You should always use a cryptographically secure random number generator, such as the one defined in the ``Crypto.Random`` module; **don't** just use the current time and the ``random`` module. :Return: An ElGamal key object (`ElGamalKey`). """ obj=ElGamalKey() # Generate a safe prime p # See Algorithm 4.86 in Handbook of Applied Cryptography obj.p = generate_probable_safe_prime(exact_bits=bits, randfunc=randfunc) q = (obj.p - 1) >> 1 # 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 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 = Integer.random_range(min_inclusive=3, max_exclusive=obj.p, randfunc=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 (obj.p-1) % obj.g == 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 = obj.g.inverse(obj.p) if safe and (obj.p-1) % ginv == 0: safe=0 if safe: break # Generate private key x obj.x = Integer.random_range(min_inclusive=2, max_exclusive=obj.p-1, randfunc=randfunc) # Generate public key y obj.y = pow(obj.g, obj.x, obj.p) return obj
def generate(bits, randfunc): """Randomly generate a fresh, new ElGamal key. The key will be safe for use for both encryption and signature (although it should be used for **only one** purpose). Args: bits (int): Key length, or size (in bits) of the modulus *p*. The recommended value is 2048. randfunc (callable): Random number generation function; it should accept a single integer *N* and return a string of random *N* random bytes. Return: an :class:`ElGamalKey` object """ obj = ElGamalKey() # Generate a safe prime p # See Algorithm 4.86 in Handbook of Applied Cryptography obj.p = generate_probable_safe_prime(exact_bits=bits, randfunc=randfunc) q = (obj.p - 1) >> 1 # Generate generator g while 1: # Choose a square residue; it will generate a cyclic group of order q. obj.g = pow( Integer.random_range(min_inclusive=2, max_exclusive=obj.p, randfunc=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 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 (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 = obj.g.inverse(obj.p) if (obj.p - 1) % ginv == 0: continue # Found break # Generate private key x obj.x = Integer.random_range(min_inclusive=2, max_exclusive=obj.p - 1, randfunc=randfunc) # Generate public key y obj.y = pow(obj.g, obj.x, obj.p) return obj