Lab 3 – RSA Encryption & Key Cracking

Overview of lab3-part1.py, lab3-part2.py, lab3-part3.py & helpers.
Lab Files Page ▶
AI‑Generated UI

Overview

This lab explores the RSA cryptosystem across three scripts: key generation + sample encryption/decryption (Part 1), cracking a provided RSA instance by factoring the modulus (Part 2), and measuring how cracking time scales with prime bit length (Part 3). A probabilistic Miller–Rabin primality test (prime.py) underpins key generation. Below you'll find runnable concepts, sample output, and notes about performance.

Repository: GitHub – RSA-Key-Cracking

Parts Summary

Part 1
Generate Keys & Encrypt

Creates two random primes p, q, computes n = p·q, λ(n)=lcm(p−1,q−1), selects e=65537, then derives d = e-1 mod λ(n). Demonstrates encrypt/decrypt in both directions.

python lab3-part1.py 64
public key is:  (n,e)
private key is: (n,d)
message:        b'test,message'
ciphered:       123456...
deciphered:     b'test,message'
Part 2
Crack Provided Ciphertext

Given a fixed public key and ciphertext C, factors n with sympy.primefactors ⇒ obtains p,q, recomputes λ(n), finds d, and deciphers M = C^d mod n ⇒ message bytes.

python lab3-part2.py
p:  ...
q:  ...
lambda(n): ...
d: ...
decrypted message: b'YouDonePassed!!'
Part 3
Timing / Scaling

Generates primes of various bit sizes, constructs n, then factors using sympy.primefactors. Repeats to average cracking time and plots bit size vs. time.

python lab3-part3.py
Testing 64-bit primes
   Test 1: 0.0021 s
   ...
Average for 64-bit
Time: 0.0020 s
...
Plot saved as rsa_crack_times.png
Helper
Miller–Rabin (prime.py)

Probabilistic primality test. gen_prime(bits) loops until a candidate passes 20 rounds of Miller–Rabin, setting top & bottom bits to ensure size & oddness.

def gen_prime(nbits):
    while True:
        p = random.getrandbits(nbits)
        p |= 2**nbits | 1
        if miller_rabin(p):
            return p

📝 Example: RSA Encryption & Decryption Process

Using Python code from lab3-part1.py

Below is a walkthrough showing how the RSA encryption and decryption process works with the Python implementation:

Step 1: Generate Keys

# From lab3-part1.py
def make_key(bit_len):
    p = gen_prime(bit_len)  # Generate prime p
    q = gen_prime(bit_len)  # Generate prime q
    while q == p:
        q = gen_prime(bit_len)  # Ensure p ≠ q
    
    n = p * q
    lm = math.lcm(p-1, q-1)  # λ(n) = lcm(p-1, q-1)
    e = 65537                # Public exponent
    d = modinv(e, lm)        # Private exponent: d = e⁻¹ mod λ(n)
    
    return ((n,e), (n,d))    # public, private

# Example output:
public_key  = (3127, 65537)   # (n, e)
private_key = (3127, 2753)    # (n, d)

Step 2: Encrypt Message

# From lab3-part1.py
msg = b'test,message'

def cipher(m, e, n):
    i = int.from_bytes(m)  # Convert bytes to integer
    return pow(i, e, n)    # C = M^e mod n

# Example:
message = b'test,message'
print("message:       ", message)
# Output: message:        b'test,message'

print("int from bytes:", int.from_bytes(message))
# Output: int from bytes: 35459967764488549030087532389

x = cipher(message, 65537, 3127)
print("ciphered:      ", x)
# Output: ciphered:       2391

Step 3: Decrypt Ciphertext

# From lab3-part1.py
def dcipher(c, d, n):
    return pow(c, d, n)    # M = C^d mod n

# Example:
y = dcipher(2391, 2753, 3127)
print("deciphered:    ", y.to_bytes(12))
# Output: deciphered:     b'test,message'

📌 Complete Example Output

$ python3 lab3-part1.py 64

public key is:  (3039956819131923800469303260782717053853, 65537)
private key is: (3039956819131923800469303260782717053853, 2834452675031667969382743586869863866433)

message:        b'test,message'
int from bytes: 35459967764488549030087532389

ciphered:       1315578006529763640141359009929019255795
deciphered:     b'test,message'

encrypted:      2597117990786094813922290830962878948057
decrypted:      b'test,message'

🔐 Digital Signatures (Reverse Process)

# From lab3-part1.py - Encrypt with PRIVATE key, decrypt with PUBLIC key
def cipher_d(m, d, n):
    i = int.from_bytes(m)
    return pow(i, d, n)    # Sign with private key

# Example:
ciph = cipher_d(b'test,message', 2753, 3127)
print("encrypted:     ", ciph)
# Output: encrypted:      2597

# Verify with public key
dciph = dcipher(2597, 65537, 3127)
print("decrypted:     ", dciph.to_bytes(12))
# Output: decrypted:      b'test,message'

💡 Key Points:

Core RSA Functions (Reference)

The lab uses raw RSA without padding. Below are the core encryption/decryption operations from the Python implementation. Production systems use larger keys and padding schemes (e.g., OAEP) for security.

Python Functions from lab3-part1.py

# Encryption: Convert message bytes to integer, then encrypt
def cipher(m_bytes, e, n):
    i = int.from_bytes(m_bytes)  # Message as integer
    return pow(i, e, n)          # C = M^e mod n

# Decryption: Decrypt ciphertext, return integer
def dcipher(c, d, n):
    return pow(c, d, n)          # M = C^d mod n

# Digital Signature: Sign with private key
def cipher_d(m_bytes, d, n):
    i = int.from_bytes(m_bytes)
    return pow(i, d, n)          # S = M^d mod n

# Helper: Modular multiplicative inverse
def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        return None              # No inverse exists
    return x % m

# Helper: Extended Euclidean Algorithm
def egcd(a, b):
    x, y, u, v = 0, 1, 1, 0
    while a != 0:
        q, r = b // a, b % a
        m, n = x - u * q, y - v * q
        b, a, x, y, u, v = a, r, u, v, m, n
    return b, x, y

RSA Flow: Key generation selects primes p, q → n = p×q → λ(n) = lcm(p−1, q−1) → choose e (typically 65537) → compute d = e−1 mod λ(n).
Encryption: C = Me mod n  |  Decryption: M = Cd mod n

How to Run Locally

PartCommandNotes
Part 1python lab3-part1.py 64Adjust bit length (≥ 32). Uses Miller–Rabin to generate primes.
Part 2python lab3-part2.pyStatic values from assignment; prints cracked message.
Part 3python lab3-part3.pyMay take minutes+ for higher bit sizes. Edit bit_sizes list to shorten.

Requirements: Python 3.9+, modules: sympy, matplotlib. Install with:

pip install sympy matplotlib

Conceptual Recap

Limitations & Extensions