Anul 3 Semestrul 1

This commit is contained in:
2025-02-06 20:33:26 +02:00
parent 0b130ee18c
commit 184f3bd92e
313 changed files with 348499 additions and 0 deletions
@@ -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)