This competition is part of the
Bradford Quantum Hackathon 2025. Please join the event on Aqora and ensure to follow the rules specified there.
Form your teams using the Team Settings Tab!
Classical and Quantum Cryptography
Overview
In cryptography, integer factorization entails finding the prime factors that multiply to produce a number ( n ), called the modulus. For small numbers, this is easy to do by hand. However, as the modulus grows bitwise, the number of possible prime pairs ( p ) and ( q ) increases astonishingly quick, making the problem of breaking encryption computationally difficult, or even infeasible.
In practical cryptographic applications, ( n ) is extremely large for security. Many systems use public and private keys:
- Public key: Used to encrypt or verify information.
- Private key: Used to decrypt or sign information.
Below are examples of classical cryptographic systems and their mathematical underpinnings.
RSA (Rivest–Shamir–Adleman)
Mathematical Foundation
RSA is based on the integer factorization problem: given ( n = p \times q ), it is computationally hard to find ( p ) and ( q ) when ( n ) is large.
n=p×q,φ(n)=(p−1)(q−1)
Encryption:
c=memodn
Decryption:
m=cdmodn
where
e×d≡1(modφ(n)).
# RSA Encryption/Decryption Example
def egcd(a, b):
if b == 0:
return a, 1, 0
g, x1, y1 = egcd(b, a % b)
return g, y1, x1 - (a // b) * y1
def modinv(a, m):
g, x, _ = egcd(a, m)
if g != 1:
raise Exception("No modular inverse")
return x % m
# Step 1: Choose primes and compute modulus
p, q = 61, 53
n = p * q
phi = (p - 1) * (q - 1)
# Step 2: Choose e and compute d
e = 17
d = modinv(e, phi)
# Step 3: Encrypt and decrypt
m = 123
c = pow(m, e, n)
decrypted = pow(c, d, n)
print(f"Public key: (n={n}, e={e})")
print(f"Private key: d={d}")
print(f"Ciphertext: {c}")
print(f"Decrypted: {decrypted}")
Public key: (n=3233, e=17)
Private key: d=2753
Ciphertext: 855
Decrypted: 123
Strengths
- Well-understood and easily implementable.
- Foundation for many encryption and signature schemes.
Weaknesses
- Security depends on the hardness of factoring ( n ).
- Requires large key sizes (2048+ bits) to be secure.
- Vulnerable to Shor’s algorithm on quantum computers.
Discrete Logarithm Problem (DLP)
Mathematical Foundation
Given a large prime ( p ), a generator ( g ), and a public value ( h = g^x \bmod p ), the challenge is to find ( x ):
gx≡h(modp)
This is computationally difficult for large ( p ) and forms the basis of Diffie–Hellman and ElGamal systems.
# DLP Brute Force Example
def discrete_log(g, h, p):
for x in range(p):
if pow(g, x, p) == h:
return x
return None
p = 23
g = 5
h = 8 # find x such that 5^x ≡ 8 mod 23
x = discrete_log(g, h, p)
print(f"p = {p}, g = {g}, h = {h}")
print(f"Solution: x = {x}")
p = 23, g = 5, h = 8
Solution: x = 6
Pollard’s Rho (Factoring Algorithm)
Mathematical Foundation
Pollard’s Rho is a probabilistic algorithm that finds nontrivial factors of a composite number ( n ) using pseudorandom sequences and the birthday paradox:
f(x)=x2+1(modn)
Iterate values and compute:
gcd(∣xi−x2i∣,n)
until a nontrivial factor is found.
# Pollard's Rho Example
import math
import random
def pollards_rho(n):
if n % 2 == 0:
return 2
f = lambda x: (x**2 + 1) % n
x, y, d = 2, 2, 1
while d == 1:
x = f(x)
y = f(f(y))
d = math.gcd(abs(x - y), n)
return None if d == n else d
n = 8051
factor = pollards_rho(n)
print(f"Found factor: {factor}")
print(f"Other factor: {n // factor}")
Found factor: 97
Other factor: 83
Elliptic Curve Cryptography (ECC)
Mathematical Foundation
ECC is based on the Elliptic Curve Discrete Logarithm Problem (ECDLP):
Given a point ( P ) on an elliptic curve and another point ( Q = kP ), it is hard to determine ( k ).
An elliptic curve over a finite field has the general form:
E:y2=x3+ax+b(modp)
# Simple ECC Example
def point_add(P, Q, a, p):
if P is None:
return Q
if Q is None:
return P
# If x-coordinates are equal and y are negatives → point at infinity
if P[0] == Q[0] and (P[1] + Q[1]) % p == 0:
return None
# Compute slope
if P == Q:
s = (3 * P[0]**2 + a) * pow(2 * P[1], -1, p) % p
else:
denom = (Q[0] - P[0]) % p
inv = pow(denom, -1, p) # Will raise ValueError if not invertible
s = ((Q[1] - P[1]) * inv) % p
# Compute resulting point
x_r = (s**2 - P[0] - Q[0]) % p
y_r = (s * (P[0] - x_r) - P[1]) % p
return (x_r, y_r)
def scalar_mult(k, P, a, p):
R = None
for bit in bin(k)[2:]:
R = point_add(R, R, a, p)
if bit == "1":
R = point_add(R, P, a, p)
return R
# Example curve: y^2 = x^3 + 2x + 3 mod 97
p = 97
a, b = 2, 3
P = (3, 6)
k = 5
Q = scalar_mult(k, P, a, p)
print(f"Base point P = {P}")
print(f"Private key k = {k}")
print(f"Public key Q = {Q}")
Base point P = (3, 6)
Private key k = 5
Public key Q = None
Quantum Computing and Shor’s Algorithm
Background
Quantum computing leverages superposition, interference, and entanglement to perform certain computations exponentially faster than classical methods.
Shor’s Algorithm (1994) efficiently factors large integers by finding the period of modular exponentiation—undermining RSA and ECC.
Mathematical Foundation
Let ( N = p \times q ). Choose ( a ) such that ( \gcd(a, N) = 1 ).
Define:
f(x)=axmodN
The period ( r ) satisfies:
ar≡1(modN)
Once ( r ) is known:
p=gcd(ar/2−1,N),q=gcd(ar/2+1,N)
# Manual Derivation Example: N = 15
import math
N = 15
a = 7
# Compute sequence
seq = [pow(a, i, N) for i in range(1, 5)]
r = 4
p = math.gcd(pow(a, r//2, N) - 1, N)
q = math.gcd(pow(a, r//2, N) + 1, N)
print("Sequence:", seq)
print(f"r = {r}, p = {p}, q = {q}")
Sequence: [7, 4, 13, 1]
r = 4, p = 3, q = 5