RSA step by step
1. What RSA is doing
RSA uses a pair of mathematically related keys:
- π Public key β can be shared with everyone.
- π Private key β must remain secret.
For encryption, the important idea is:
Alice Bob
wants to send "7" owns the key pair
βββββββββββββββ
β PUBLIC KEY β
β (n, e) β
ββββββββ¬βββββββ
β
public key βββββββββββββββββββββββββ
β
βΌ
βββββββββββ
β message β
β 7 β
ββββββ¬βββββ
β
β use public key
β
βΌ
βββββββββββββββββββββ
β ENCRYPT β
β β
β c = m^e mod n β
βββββββββββ¬ββββββββββ
β
βΌ
ββββββββββββ
βciphertextβ
β 13 β
ββββββ¬ββββββ
β
β send over network
β
βββββββββββββββββΌβββββββββββββββββββββββββββββββΆ
β
Bob
β
βΌ
βββββββββββββββ
β PRIVATE KEY β
β (n, d) β
ββββββββ¬βββββββ
β
βΌ
βββββββββββββββββββββ
β DECRYPT β
β β
β m = c^d mod n β
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββ
β message β
β 7 β
βββββββββββ
The interesting property is:
PUBLIC PRIVATE
message βββββββΆ RSA operation βββββββΆ ciphertext βββββββΆ RSA operation
7 using e 13 using d
β
βΌ
7
The public key can therefore be given to anyone:
Public key
β
βββ n
βββ e
while the private key stays with the owner:
Private key
β
βββ n
βββ d
For now, we will ignore how the keys were generated.
Assume somebody has already given us:
public_key = {55, 3}
private_key = {55, 27}
That means:
Public key
(n, e)
(55, 3)
Private key
(n, d)
(55, 27)
Let's encrypt the number 7.
message = 7
{n, e} = public_key
ciphertext =
message
|> Integer.pow(e)
|> rem(n)
ciphertext
Result:
13
So this happened:
message
7
β
β public key (55, 3)
βΌ
7Β³ mod 55
β
βΌ
13
ciphertext
Now decrypt it with the private key:
{n, d} = private_key
decrypted_message =
ciphertext
|> Integer.pow(d)
|> rem(n)
decrypted_message
Result:
7
So the complete journey was:
PUBLIC KEY PRIVATE KEY
(55, 3) (55, 27)
β β
βΌ βΌ
7 ββββββββββΆ 7Β³ mod 55 ββββββΆ 13 ββββββΆ 13Β²β· mod 55 ββββββΆ 7
message ciphertext message
Or mathematically:
Encryption:
c = m^e mod n
c = 7Β³ mod 55
c = 13
and:
Decryption:
m = c^d mod n
m = 13Β²β· mod 55
m = 7
Why does this work?
7
β
β raise to e
βΌ
7^e mod n
β
β raise to d
βΌ
7 again
And where did these numbers come from?
n = 55
e = 3
d = 27
That is what we will build step by step.
The journey starts with two prime numbers:
p = 5 q = 11
\ /
\ /
\ /
βΌ βΌ
n = p Γ q
n = 55
From p and q, we will eventually construct:
p q
\ /
\ /
ββββββββ¬ββββββ
β
βΌ
n
β
ββββββββ΄βββββββ
β β
βΌ βΌ
e d
β β
βΌ βΌ
PUBLIC KEY PRIVATE KEY
(n, e) (n, d)
Important: this notebook is implementing RSA with tiny numbers to understand the mathematics. It is not an implementation that should be used for real cryptography.
Main Parts
Key generation
ββββββββββββββββ
β KEY GENERATIONβ
ββββββββ¬ββββββββ
β
βββββββββββ΄ββββββββββ
β β
βΌ βΌ
secret p secret q
β β
βββββββββββ¬ββββββββββ
β
βΌ
n = p Γ q
β
βΌ
derive e and d
β
βββββββββββ΄ββββββββββ
β β
βΌ βΌ
ββββββββββββββββ ββββββββββββββββ
β PUBLIC KEY β β PRIVATE KEY β
β (n, e) β β (n, d) β
ββββββββββββββββ ββββββββββββββββ
Encryption and decryption
βββββββββββββββ
β MESSAGE β
ββββββββ¬βββββββ
β
βΌ
convert / pad message
β
βΌ
βββββββββββββββ
β PUBLIC KEY β
β (n, e) β
ββββββββ¬βββββββ
β
β c = m^e mod n
βΌ
βββββββββββββββ
β CIPHERTEXT β
ββββββββ¬βββββββ
β
βΌ
βββββββββββββββ
β PRIVATE KEY β
β (n, d) β
ββββββββ¬βββββββ
β
β m = c^d mod n
βΌ
βββββββββββββββ
β MESSAGE β
β recovered β
βββββββββββββββ
Section
What is a prime number?
# A prime number has exactly two positive divisors: 1 and itself
prime_numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23]
# 11 = 1x11 -> prime, because it is only divisible by 1 and itself
# 12 = 2 Γ 6 = 3 Γ 4 -> not a prime, because it is divisible by 1, 2, 3, 4, 6, itself
RSA chooses two primes
# Requirements for the prime numbers:
# 1. They must be different p != q
# 2. They must be big (e.g. ~1024 bits each or ~300 decimal digits). NOTE: 2^1024 ~ 10^308
# 3. They must be secret
p = 11 # secret
q = 17 # secret
RSA multiplies them
# The product of these two primes is ~2048 bits number or ~600 decimal digits
# RSA reveals the product while hiding its factors.
n = p * q # public
# NOTE: If the numbers are not big you can easily find them
# Is n divisible by 2? -> no.
# Is n divisible by 3? -> no.
# Is n divisible by 5? -> no.
# Is n divisible by 7? -> no.
# Is n divisible by 11? -> yes. Thus p = 11 and q = n / p = 187 / 11 = 17.
# Why different p and q?
# Let's pick p and q to be equal to 4957
# p1 = 4957
# n1 = p1 * p1
# :math.sqrt(n1)
# Then only thing we need to do is to take the square root of n and we will find p.
# What is the role of n?
# 1. It is the modulus used by RSA arithmetic.
# 2. It hides p and q behind the difficult integer-factorisation problem.
Why primes rather than arbitrary numbers?
# This is partly because prime numbers give RSA very clean mathematical structure.
# Prime numbers allows us to use #Euler's totient function β Ο(n). https://en.wikipedia.org/wiki/Euler%27s_totient_function
# Ο(n) tells us: How many positive integers smaller than n are coprime with n?
What does coprime mean?
# Two numbers are coprime if their greatest common divisor is 1.
a = 8 # divisors 1, 2, 4, 8
b = 15 # divisors 1, 3, 5, 15
# a and b are coprime because the only factor they share is 1.
Integer.gcd(8, 15) # greatest common divisor
# Integer.gcd(6, 15) # 6 -> 1, 2, 3, 6
# NOTE: the coprimes don't need to be prime themselves
What does Ο(n) actually count?
# take n = 10
# Ο(n) looks at
# 1 2 3 4 5 6 7 8 9
# Checks if they are coprime
# 1 β gcd(1,10) = 1 β
# 2 β 2 β
# 3 β 1 β
# 4 β 2 β
# 5 β 5 β
# 6 β 2 β
# 7 β 1 β
# 8 β 2 β
# 9 β 1 β
# Integer.gcd(5, 10)
# So the coprimes are 1, 3, 7, 9. There are four.
# Therefore Ο(10) = 4
Ο(p) is especially simple when p is prime
# Suppose p = 7; a prime number
# Every positive number smaller than 7 is coprime with 7:
# 1, 2, 3, 4, 5, 6
# This is because the prime number has as a divisor only 1 and itself.
# So none of the numbers 1..6 can share a divisor with it other than 1.
# Ο(7) = 6
# More generally when p is prime Ο(p) = p - 1.
# This fact is one reason primes are so convenient in RSA.
Now return to our RSA example
p = 11
q = 17
n = p * q # 187 = 11x17
# What is Ο(187)?
# 187 = 11x17
# A number fails to be coprime with 187 only if it is divisible by:
# 11 or 17
# Let's look at multiples of 11
# The only numbers in the range 1..186 that are divisible by 11 are
# 11
# 22
# 33
# 4x11
# ...
# 15x11 = 165
# 16x11 = 176
# 17x11 = 187 this is bigger than 186 thus we stop at 16.
# So we eliminate 16 numbers.
# Let's now look at multiples of 17
# 17
# 34
# 3x17
# ...
# 10x17 = 170
# 11x17 = 187 this is bigger than 186 thus we stop at 10.
# So we eliminate 10 numbers.
# We count what remains 186 - 16 - 10 = 160
# Therefore Ο(187) = 160
Where does the famous RSA formula come from?
# Let's start with n = p * q
# Among the numbers 1...p*q, there are:
# q multiples of p:
# p, 2p, 3p, β¦, q*p
# and p multiples of q:
# q, 2q, 3q, β¦, p*q
# But p * q appears in both lists, so we've counted it twice.
# Therefore the number divisible by p or q is:
# p + q β 1
# There are p*q numbers total, so the number coprime to p*q is:
# p * q β (p + q β 1)
# Expand:
# p * q - p - q + 1
# Factor:
# (p - 1) * (q - 1)
#
# Ο(n) = Ο(p*q) = (p - 1) * (q - 1)
# Ο(187) = Ο(11*17) = (11 - 1) * (17 - 1) = 10 * 16 = 160
# NOTE: This works only if the p and q are prime
But why does RSA care about this count?
# When working modulo n, the numbers coprime with n have a special property:
# They have multiplicative inverses modulo n.
What does a modulo n mean?
# Modulo n means we only care about the remainder after division by n.
# For example, modulo 10:
# 27 / 10 = 2 remainder 7
# 27 mod 10 = 7
# 7 mod 10 = 7
# This means 27 and 7 are equivalent when working modulo 10.
Integer.mod(27, 10) == Integer.mod(7, 10)
# The numbers therefore "wrap around" every 10:
# 7
# 17
# 27
# 37
# 47
# ...
# all are 7 modulo 10
Enum.map([7, 17, 27, 37, 47, 57], &Integer.mod(&1, 10))
|> IO.inspect(charlists: :as_lists)
# You can think of modular arithmetic a little like a clock:
# modulo 10
# 0 β 1 β 2 β 3 β ... β 9
# β β
# βββββββββββββββββββββββββ
# After reaching 9, adding 1 takes us back to 0.
# For RSA, this "wrapping around" is important
# because almost all of the core calculations happen modulo some number.
What does a multiplicative inverse modulo n mean?
# Normally, a multiplicative inverse is a number that gets us back to 1.
# For example the inverse of 5 is 1/5.
# 5 * 1/5 = 1.
# In modular arithmetic we ask a slightly different question:
# For a number a, can we find another integer x such that their product becomes 1
# after taking modulo n?
# Mathematically:
# a Γ x β‘ 1 (mod n)
# For example let's work modulo 10.
# Let's take a = 3
# The multiplicative inverse of 3 modulo 10 is 7:
# 3 Γ 7 = 21
# 21 mod 10 = 1
Integer.mod(3 * 7, 10)
# But not every number has an inverse.
# For example, 2 has no multiplicative inverse modulo 10.
# The reason is:
# gcd(2, 10) = 2
Integer.gcd(2, 10)
# They are not coprime!
# A modular inverse exists exactly when:
# gcd(a, n) = 1
# a and n need to be coprime
Why does RSA care?
# Earlier we calculated:
# n = 187
# Ο(187) = 160
# RSA needs to choose a public exponent `e` that is coprime with 160.
# For example:
# e = 7
# gcd(7, 160) = 1
Integer.gcd(7, 160)
# Therefore 7 has a multiplicative inverse modulo 160.
# That inverse is:
# d = 23
# because:
# 7 Γ 23 = 161
# 161 mod 160 = 1
Integer.gcd(23, 160)
defmodule RsaLab.Math do
# Euler's totient (phi) for n = p * q, where p and q are distinct primes.
def phi_from_primes(p, q) do
(p - 1) * (q - 1)
end
end
p = 11
q = 17
n = p * q
phi = RsaLab.Math.phi_from_primes(p, q)
%{
p: p,
q: q,
n: n,
phi: phi
}
1 and itself