Anul 3 Semestrul 1
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
#Greatest Common Divisor First Method - Euclid's Algorithm - recurisive
|
||||
def gcd1(a,b):
|
||||
if a == 0 and b != 0:
|
||||
return b
|
||||
if a != 0 and b == 0:
|
||||
return a
|
||||
#check if the numbers are positive integers, if not raise an error
|
||||
if a < 1 or b < 1:
|
||||
raise ValueError("Both numbers must be positive integers")
|
||||
return helper_gcd1(a,b)
|
||||
|
||||
def helper_gcd1(a,b):
|
||||
#Euclid's Algorithm: If one number is 0, the other number is the GCD. If not, divide the larger number by the smaller number, then divide the divisor by the remainder, repeat until the remainder
|
||||
if b == 0:
|
||||
return a
|
||||
if a == 0:
|
||||
return b
|
||||
if a > b:
|
||||
return helper_gcd1(a%b, b)
|
||||
else:
|
||||
return helper_gcd1(a, b%a)
|
||||
|
||||
#Greatest Common Divisor Second Method - Repeat Subtraction
|
||||
def gcd2(a,b):
|
||||
#check if the numbers are positive integers, if not raise an error
|
||||
if a == 0 and b != 0:
|
||||
return b
|
||||
if a != 0 and b == 0:
|
||||
return a
|
||||
if a < 1 or b < 1:
|
||||
raise ValueError("Both numbers must be positive integers")
|
||||
#Euclid's Algorithm: If one
|
||||
while a != b:
|
||||
if a > b:
|
||||
a -= b
|
||||
else:
|
||||
b -= a
|
||||
return a
|
||||
|
||||
#Greatest Common Divisor Third Method - Euclid's Algorithm
|
||||
def gcd3(a,b):
|
||||
#check if the numbers are positive integers, if not raise an error
|
||||
if a == 0 and b != 0:
|
||||
return b
|
||||
if a != 0 and b == 0:
|
||||
return a
|
||||
if a < 1 or b < 1:
|
||||
raise ValueError("Both numbers must be positive integers")
|
||||
#Euclid's Algorithm: Divide the larger number by the smaller number, then divide the divisor by the remainder, repeat until the remainder
|
||||
while b:
|
||||
a,b = b,a%b
|
||||
return a
|
||||
|
||||
#Time Function
|
||||
import time
|
||||
from tabulate import tabulate
|
||||
def time_function(function):
|
||||
#input values for gcd
|
||||
input1 = [13, 18, 100, 252, 105, 625, 1000, 10000, 100000, 1000000, 3]
|
||||
input2 = [5, 24, 75, 147, 200, 500, 2100, 20000, 200000, 2000200, 0]
|
||||
#expected results for gcd
|
||||
expected = [1, 6, 25, 21, 5, 125, 100, 10000, 100000, 200,3]
|
||||
#results and times of running the gcd functions
|
||||
ret = []
|
||||
times = []
|
||||
for i in range(len(input1)):
|
||||
start = time.time() #start time
|
||||
ret.append(function(input1[i], input2[i])) #run the function and append the result
|
||||
end = time.time() #end time
|
||||
times.append(end-start) #calculate the time taken
|
||||
#print the results nicely
|
||||
labels2 = ["Expected", "Results", "Times"]
|
||||
table = [expected, ret, times]
|
||||
print(tabulate(table, showindex=labels2, tablefmt="fancy_grid"))
|
||||
|
||||
|
||||
def main():
|
||||
time_function(gcd1)
|
||||
time_function(gcd2)
|
||||
time_function(gcd3)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,54 @@
|
||||
#7. Algorithm for determining all Carmichael numbers less than a given bound.
|
||||
|
||||
import math
|
||||
|
||||
# Helper function to find all prime factors of a number
|
||||
def prime_factors(n):
|
||||
factors = set()
|
||||
# Check for divisibility by 2
|
||||
while n % 2 == 0:
|
||||
factors.add(2)
|
||||
n //= 2
|
||||
# Check for odd factors from 3 upwards
|
||||
for i in range(3, int(math.sqrt(n)) + 1, 2):
|
||||
while n % i == 0:
|
||||
factors.add(i)
|
||||
n //= i
|
||||
# If n is prime and greater than 2 then add it to the set
|
||||
if n > 2:
|
||||
factors.add(n)
|
||||
return factors
|
||||
|
||||
# Function to check if a number is square-free (no repeated prime factors)
|
||||
def is_square_free(n, prime_factors):
|
||||
for p in prime_factors:
|
||||
if n % (p * p) == 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Function to check for Carmichael numbers
|
||||
def is_carmichael(n):
|
||||
prime_factors_of_n = prime_factors(n)
|
||||
if(len(prime_factors_of_n)<2):
|
||||
return False
|
||||
if n < 3 or not is_square_free(n, prime_factors_of_n):
|
||||
return False
|
||||
for p in prime_factors_of_n:
|
||||
if (n - 1) % (p - 1) != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Function to find all Carmichael numbers below a given bound
|
||||
def carmichael_numbers_below(bound):
|
||||
carmichaels = []
|
||||
for n in range(3, bound):
|
||||
if is_carmichael(n):
|
||||
carmichaels.append(n)
|
||||
return carmichaels
|
||||
|
||||
bound = 552722
|
||||
carmichaels = carmichael_numbers_below(bound)
|
||||
print(f"Carmichael numbers below {bound}:")
|
||||
print(carmichaels)
|
||||
|
||||
#https://oeis.org/A002997
|
||||
@@ -0,0 +1,39 @@
|
||||
#1. Miller-Rabin algorithm. It will work for numbers of arbitrary size.
|
||||
|
||||
import random
|
||||
|
||||
def miller_rabin(n, k=5) -> bool:
|
||||
if n % 2 == 0 or n <= 2:
|
||||
return False
|
||||
s, t = step1(n)
|
||||
for _ in range(k):
|
||||
a: int = random.randint(2, n - 2)
|
||||
x: int = pow(a, t, n)
|
||||
if x == 1:
|
||||
continue
|
||||
for _ in range(s):
|
||||
y: int = pow(x, 2, n)
|
||||
if y == 1 and x != 1 and x != n - 1:
|
||||
return False
|
||||
x = y
|
||||
if y != 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def step1(n) -> tuple[int, int]:
|
||||
t: int = n - 1
|
||||
s = 0
|
||||
while t % 2 == 0:
|
||||
t //= 2
|
||||
s += 1
|
||||
return s, t
|
||||
|
||||
def main():
|
||||
print(miller_rabin(13))
|
||||
print(miller_rabin(15))
|
||||
print(miller_rabin(2**1279 - 1))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,101 @@
|
||||
import random
|
||||
from sympy import isprime, gcd
|
||||
|
||||
# Define the 27-character alphabet mapping
|
||||
ALPHABET = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
ALPHABET_MAP = {char: idx for idx, char in enumerate(ALPHABET)}
|
||||
REVERSE_ALPHABET_MAP = {idx: char for idx, char in enumerate(ALPHABET)}
|
||||
|
||||
|
||||
# Extended Euclidean Algorithm to find modular inverse
|
||||
def modular_inverse(a, m):
|
||||
"""Returns the modular inverse of a under modulo m, or None if it doesn't exist."""
|
||||
m0, x0, x1 = m, 0, 1
|
||||
while a > 1:
|
||||
q = a // m
|
||||
a, m = m, a % m
|
||||
x0, x1 = x1 - q * x0, x0
|
||||
if x1 < 0:
|
||||
x1 += m0
|
||||
return x1
|
||||
|
||||
|
||||
# RSA key generation function
|
||||
def generate_rsa_keys(bit_length=512):
|
||||
p = generate_large_prime(bit_length)
|
||||
q = generate_large_prime(bit_length)
|
||||
|
||||
n = p * q
|
||||
phi = (p - 1) * (q - 1) # euler's function
|
||||
|
||||
e = 65537
|
||||
while gcd(e, phi) != 1:
|
||||
e = random.randint(2, phi - 1)
|
||||
|
||||
d = modular_inverse(e, phi)
|
||||
if d is None:
|
||||
raise ValueError("Modular inverse for the chosen e does not exist.")
|
||||
|
||||
return (n, e), (n, d)
|
||||
|
||||
|
||||
def generate_large_prime(bit_length):
|
||||
"""Generates a random prime number of approximately bit_length bits."""
|
||||
while True:
|
||||
candidate = random.getrandbits(bit_length)
|
||||
if isprime(candidate):
|
||||
return candidate
|
||||
|
||||
|
||||
# RSA encryption
|
||||
def rsa_encrypt(plaintext, public_key):
|
||||
n, e = public_key
|
||||
# Convert plaintext to numeric format using the alphabet map
|
||||
numeric_plaintext = [ALPHABET_MAP[char] for char in plaintext if char in ALPHABET_MAP]
|
||||
# Encrypt each character in the numeric plaintext
|
||||
ciphertext = [pow(char, e, n) for char in numeric_plaintext]
|
||||
return ciphertext
|
||||
|
||||
|
||||
# RSA decryption
|
||||
def rsa_decrypt(ciphertext, private_key):
|
||||
n, d = private_key
|
||||
# Decrypt each character in the ciphertext
|
||||
decrypted_numbers = [pow(char, d, n) for char in ciphertext]
|
||||
# Convert decrypted numbers back to characters
|
||||
plaintext = ''.join(REVERSE_ALPHABET_MAP[num] for num in decrypted_numbers)
|
||||
return plaintext
|
||||
|
||||
|
||||
# Validation functions
|
||||
def validate_plaintext(plaintext):
|
||||
"""Validates that the plaintext contains only characters from the 27-character alphabet."""
|
||||
for char in plaintext:
|
||||
if char not in ALPHABET_MAP:
|
||||
raise ValueError(f"Invalid character in plaintext: {char}")
|
||||
|
||||
|
||||
def validate_ciphertext(ciphertext, n):
|
||||
"""Validates that each part of the ciphertext is a number less than n."""
|
||||
for part in ciphertext:
|
||||
if not (0 <= part < n):
|
||||
raise ValueError(f"Invalid ciphertext value: {part}")
|
||||
|
||||
|
||||
# Example usage
|
||||
if __name__ == "__main__":
|
||||
# Generate keys
|
||||
public_key, private_key = generate_rsa_keys()
|
||||
|
||||
# Plaintext to encrypt
|
||||
plaintext = "HELLO WORLD"
|
||||
validate_plaintext(plaintext)
|
||||
|
||||
# Encrypt the plaintext
|
||||
ciphertext = rsa_encrypt(plaintext, public_key)
|
||||
print("Ciphertext:", ciphertext)
|
||||
|
||||
# Validate ciphertext and decrypt
|
||||
validate_ciphertext(ciphertext, public_key[0])
|
||||
decrypted_text = rsa_decrypt(ciphertext, private_key)
|
||||
print("Decrypted Text:", decrypted_text)
|
||||
@@ -0,0 +1,79 @@
|
||||
#2. Paillier cryptosystem
|
||||
|
||||
import random
|
||||
from math import gcd, lcm
|
||||
from sympy import isprime, public
|
||||
|
||||
ALPHABET = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
ALPHABET_MAP = {char: idx for idx, char in enumerate(ALPHABET)}
|
||||
REVERSE_ALPHABET_MAP = {idx: char for idx, char in enumerate(ALPHABET)}
|
||||
|
||||
def text_to_numeric(plaintext):
|
||||
return [ALPHABET_MAP[char] for char in plaintext if char in ALPHABET_MAP]
|
||||
|
||||
def numeric_to_text(numeric_plaintext):
|
||||
return "".join([REVERSE_ALPHABET_MAP[idx] for idx in numeric_plaintext])
|
||||
|
||||
|
||||
def generate_paillier_keypair(bit_length=512):
|
||||
p = generate_large_prime(bit_length)
|
||||
q = generate_large_prime(bit_length)
|
||||
n = p * q
|
||||
n_squared = n * n
|
||||
|
||||
lambda_n = lcm(p - 1, q - 1)
|
||||
|
||||
mmi = 0
|
||||
|
||||
while True:
|
||||
g = random.randint(1, n_squared-1)
|
||||
if g // n == g / n:
|
||||
continue
|
||||
mmi = pow(L(pow(g, lambda_n, n_squared), n),-1,n)
|
||||
break
|
||||
|
||||
public_key = (n, g)
|
||||
private_key = (lambda_n, mmi)
|
||||
return public_key, private_key
|
||||
|
||||
def L(x, n):
|
||||
return (x - 1) // n
|
||||
|
||||
def generate_large_prime(bit_length):
|
||||
while True:
|
||||
candidate = random.getrandbits(bit_length)
|
||||
if isprime(candidate):
|
||||
return candidate
|
||||
|
||||
def paillier_encrypt(plaintext, public_key):
|
||||
n, g = public_key
|
||||
r = random.randint(1, n-1)
|
||||
while gcd(r, n) != 1:
|
||||
# Extremly unlikely to happen
|
||||
r = random.randint(1, n-1)
|
||||
ciphertext = []
|
||||
for int_letter in text_to_numeric(plaintext):
|
||||
c = (pow(g, int_letter, n*n) * pow(r, n, n*n)) % (n*n)
|
||||
ciphertext.append(c)
|
||||
return ciphertext
|
||||
|
||||
|
||||
def paillier_decrypt(ciphertext, private_key, public_key):
|
||||
lambda_n, mmi = private_key
|
||||
n, g = public_key
|
||||
plaintext = []
|
||||
for c in ciphertext:
|
||||
l = L(pow(c, lambda_n, n*n), n)
|
||||
m = (l * mmi) % n
|
||||
plaintext.append(m)
|
||||
return numeric_to_text(plaintext)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
public_key, private_key = generate_paillier_keypair()
|
||||
plaintext = "HELLO WORLD"
|
||||
print("Plaintext:", plaintext)
|
||||
ciphertext = paillier_encrypt(plaintext, public_key)
|
||||
print("Ciphertext:", ciphertext)
|
||||
decrypted = paillier_decrypt(ciphertext, private_key, public_key)
|
||||
print("Decrypted:", decrypted)
|
||||
Reference in New Issue
Block a user