kryptografia/2/1.py
2024-11-03 20:44:17 +01:00

121 lines
3.9 KiB
Python

import gmpy2
from gmpy2 import mpz, is_prime, powmod, invert, random_state, mpz_random
import random
# Task 1: Generate a random elliptic curve over F_p
def generate_random_curve(p):
p = mpz(p)
rs = random_state(random.randrange(2**32))
while True:
A = mpz_random(rs, p)
B = mpz_random(rs, p)
# Check that the discriminant is non-zero
discriminant = (4 * powmod(A, 3, p) + 27 * powmod(B, 2, p)) % p
if discriminant != 0:
return A, B
# Task 2: Find a random point on the elliptic curve over F_p
def find_random_point(A, B, p):
p = mpz(p)
rs = random_state(random.randrange(2**32))
while True:
x = mpz_random(rs, p)
rhs = (powmod(x, 3, p) + A * x + B) % p
legendre_symbol = gmpy2.legendre(rhs, p)
if legendre_symbol == 1:
# Compute y using the fact that p ≡ 3 mod 4
y = powmod(rhs, (p + 1) // 4, p)
return x, y
elif legendre_symbol == 0:
# y = 0 is a valid solution
y = mpz(0)
return x, y
# If rhs is not a quadratic residue, try another x
# Task 3: Compute the inverse (negative) of a given point
def point_negation(P, p):
x, y = P
return x, (-y) % p
# Task 4: Compute P ⊕ Q, the sum of two points on the elliptic curve
def point_addition(P, Q, A, p):
if P == 'O':
return Q
if Q == 'O':
return P
x1, y1 = P
x2, y2 = Q
if x1 == x2 and (y1 + y2) % p == 0:
# P + (-P) = O
return 'O'
if x1 == x2 and y1 == y2:
# Point doubling
if y1 == 0:
return 'O'
numerator = (3 * powmod(x1, 2, p) + A) % p
denominator = (2 * y1) % p
else:
# P ≠ Q
numerator = (y2 - y1) % p
denominator = (x2 - x1) % p
inv_denominator = invert(denominator, p)
if inv_denominator is None:
return 'O' # Cannot invert denominator; result is point at infinity
lam = (numerator * inv_denominator) % p
x3 = (powmod(lam, 2, p) - x1 - x2) % p
y3 = (lam * (x1 - x3) - y1) % p
return x3, y3
# Task 5: Compute nP, the scalar multiplication of a point
def scalar_multiplication(P, n, A, p):
n = mpz(n)
result = 'O' # Start with the point at infinity
addend = P
while n > 0:
if n % 2 == 1:
result = point_addition(result, addend, A, p)
addend = point_addition(addend, addend, A, p)
n = n // 2
return result
# Helper function to generate a large prime p ≡ 3 mod 4
def generate_large_prime(bits):
rs = random_state(random.randrange(2**32))
while True:
candidate = mpz_random(rs, 2 ** bits)
candidate |= 1 # Ensure it's odd
candidate |= 3 # Ensure p ≡ 3 mod 4
candidate = gmpy2.next_prime(candidate)
if candidate % 4 == 3 and candidate.bit_length() == bits:
return candidate
# Example usage of the functions
if __name__ == "__main__":
# Generate a large prime p ≡ 3 mod 4 (approximately 300 bits)
bits = 300
p = generate_large_prime(bits)
print(f"Generated prime p = {p}")
# Task 1: Generate random curve coefficients A and B
A, B = generate_random_curve(p)
print(f"Random curve coefficients: A = {A}, B = {B}")
# Task 2: Find a random point on the curve
P = find_random_point(A, B, p)
print(f"Random point P = {P}")
# Task 3: Compute the inverse of point P
negative_P = point_negation(P, p)
print(f"Inverse of P: -P = {negative_P}")
# Task 4: Add two points P and Q on the curve
Q = find_random_point(A, B, p)
print(f"Another random point Q = {Q}")
R = point_addition(P, Q, A, p)
print(f"Sum of P and Q: R = P ⊕ Q = {R}")
# Task 5: Compute nP for a random scalar n
n = mpz_random(random_state(random.randrange(2**32)), p)
nP = scalar_multiplication(P, n, A, p)
print(f"Scalar multiplication: {n} * P = {nP}")