I'm working on a RSA encryption and decryption project in python. I've written the following code, but I'm looking for suggest on how to improve it. Specifically, I'm interested in optimizing performance, enhancing security, and following best practise. Any feedback would be greatly appreciated!
import random
import math
# n= p.q
# phi(n) = (p-1)(q-1)
# Prime number checking function
def is_prime(number):
if number < 2:
return False
for i in range(2, int(math.sqrt(number)) + 1): # Optimized to check up to sqrt(number)
if number % i == 0:
return False
return True
# Generate a random prime between a range
def generate_prime(min_value, max_value):
prime = random.randint(min_value, max_value)
while not is_prime(prime):
prime = random.randint(min_value, max_value)
return prime
# Modular inverse using the Extended Euclidean Algorithm
def mod_inverse(e, phi):
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
g, x, y = egcd(e, phi)
if g != 1:
raise ValueError("Modular inverse does not exist")
else:
return x % phi
# Function to encrypt message in chunks
def encrypt_message(message, e, n, block_size):
message_encoded = [ord(c) for c in message]
chunks = [message_encoded[i:i + block_size] for i in range(0, len(message_encoded), block_size)]
ciphertext = []
for chunk in chunks:
# Convert chunk to an integer (ASCII representation)
m = int(''.join([f'{c:03d}' for c in chunk]))
# Encrypt using (m ^ e) mod n
ciphertext.append(pow(m, e, n))
return ciphertext
# Function to decrypt message in chunks
def decrypt_message(ciphertext, d, n, block_size):
message_decoded = []
for cipher_chunk in ciphertext:
# Decrypt using (c ^ d) mod n
m = pow(cipher_chunk, d, n)
# Convert back to individual characters from the integer
chunk_str = str(m).zfill(block_size * 3) # Zfill ensures we get proper block size
message_decoded.extend([int(chunk_str[i:i + 3]) for i in range(0, len(chunk_str), 3)])
return ''.join([chr(c) for c in message_decoded])
# RSA key generation
p, q = generate_prime(1000, 50000), generate_prime(1000, 50000)
while p == q:
q = generate_prime(1000, 50000)
n = p * q
phi_n = (p - 1) * (q - 1)
e = random.randint(3, phi_n - 1)
while math.gcd(e, phi_n) != 1:
e = random.randint(3, phi_n - 1)
d = mod_inverse(e, phi_n)
print("Prime number P: ", p)
print("Prime number Q: ", q)
print("Public Key (e, n): ", (e, n))
print("Private Key (d, n): ", (d, n))
print("n: ", n)
print("Phi of n: ", phi_n)
# Example message
message = "Khoa Toan-Co-Tin hoc, Truong Dai hoc Khoa hoc Tu nhien"
block_size = len(str(n)) // 3 - 1 # Block size in terms of number of characters (each char takes 3 digits in ASCII)
# Encrypt message
ciphertext = encrypt_message(message, e, n, block_size)
print("Ciphertext: ", ciphertext)
# Decrypt message
decrypted_message = decrypt_message(ciphertext, d, n, block_size)
print("Decrypted Message: ", decrypted_message)
What improvements can I make to this code?
numpy
, but don't usenumpy
. Also: the most obvious best-practice/security advice is to not do any of this and use a vetted library instead. \$\endgroup\$performance
but haven't shown any evidence of profiling or benchmarking. This code is essentially guaranteed to be slow when compared to C implementations (including readily-available C implementations with Python bindings). \$\endgroup\$