Code:
# Import necessary modules from PyCryptodome and standard libraries
from Crypto.Util.number import getPrime, getRandomNBitInteger # For prime number
generation
from Crypto.PublicKey import ElGamal # ElGamal key management
import random # For secure random number generation
def encrypt_message(public_key, message):
# Unpack public key components (prime, generator, public element)
p, g, y = public_key
# Convert message bytes to integer (big-endian encoding)
plaintext = int.from_bytes(message, 'big')
# Generate random ephemeral key (k) in range [1, p-2]
k = random.randint(1, p - 2)
# Compute first ciphertext component: c1 = g^k mod p
c1 = pow(g, k, p)
# Compute second ciphertext component: c2 = (plaintext * y^k) mod p
c2 = (plaintext * pow(y, k, p)) % p
# Return ciphertext as a tuple of the two components
return (c1, c2)
def decrypt_message(private_key, ciphertext):
# Unpack private key components (prime, private exponent)
p, x = private_key
# Unpack ciphertext components
c1, c2 = ciphertext
# Compute plaintext: c2 * (c1^(p-1-x)) mod p
# This uses Fermat's little theorem for modular inverse calculation
plaintext = (c2 * pow(c1, p - 1 - x, p)) % p
# Convert integer plaintext back to bytes
message = plaintext.to_bytes((plaintext.bit_length() + 7) // 8, 'big')
return message
# Main program loop
print("ElGamal Encryption/Decryption")
while True:
# Display menu options
print("\nMenu:")
print("1. Generate keys")
print("2. Encrypt")
print("3. Decrypt")
print("4. Exit")
choice = input("Enter your choice: ")
if choice == "1":
# Generate 2048-bit prime number (sufficient for security)
p = getPrime(2048)
# Generate private key (random integer in [1, p-2])
x = random.randint(1, p - 2)
# Use fixed generator (common choice for ElGamal)
g = 2
# Compute public key component: y = g^x mod p
y = pow(g, x, p)
# Store key pairs
public_key = (p, g, y)
private_key = (p, x)
print("Keys generated successfully!")
elif choice == "2":
# Check if keys have been generated first
if 'public_key' not in locals():
print("Please generate keys first!")
else:
# Get user input and encode to bytes
message = input("Enter plaintext: ").encode()
# Perform encryption
ciphertext = encrypt_message(public_key, message)
print("Ciphertext:", ciphertext)
elif choice == "3":
# Check if keys have been generated first
if 'private_key' not in locals():
print("Please generate keys first!")
else:
try:
# Get ciphertext input from user
ciphertext_input = input("Enter ciphertext (in the format (c1, c2)): ")
# Safely parse input without using eval()
c1, c2 = map(int, ciphertext_input.strip('()').split(','))
ciphertext = (c1, c2)
# Decrypt and convert to string
plaintext = decrypt_message(private_key, ciphertext)
print("Plaintext:", plaintext.decode())
except Exception as e:
# Handle invalid input formats or decryption errors
print("Invalid ciphertext format or decryption error:", e)
elif choice == "4":
# Exit the program
print("Exiting...")
break
else:
# Handle invalid menu choices
print("Invalid choice! Please try again.")
Output ScreenShot: