Skip to content

Instantly share code, notes, and snippets.

@joseph-lozano
Last active December 13, 2015 01:16
Show Gist options
  • Save joseph-lozano/a2f3ecead08189e8cc45 to your computer and use it in GitHub Desktop.
Save joseph-lozano/a2f3ecead08189e8cc45 to your computer and use it in GitHub Desktop.
Simple implementation of RSA public-key encryption and private-key decryption. Not very secure
require 'pry'
# Extend Integers for prime number methods
class Integer < Numeric
def prime?
2.upto((self**0.5).to_i) { |n| return false if self % n == 0 }
true
end
def next_prime
next_prime = self + 1
next_prime += 1 until next_prime.prime?
next_prime
end
def prime_factors
return [self] if self < 2
factors = []
2.upto(self) { |m| factors << m if self % m == 0 && m.prime? }
factors
end
end
# Simple implentations of RSA public key encryption
class RSA
def initialize(primes = [])
if (primes.class != Array && primes.length != 2) && (!primes[0].prime? || !primes[1].prime?)
p 'can only initialize RSA with array of two prime numbers'
end
@e = 3
if primes == []
@p, @q = generate_primes
else
@p, @q = primes[0], primes[1]
end
end
def encrypt(public_key, message)
message.bytes.map do |byte|
byte**public_key[0] % public_key[1]
end
end
def decrypt(crypt)
crypt.map do |byte|
char = byte**private_key % n
[char].pack('c')
end.join
end
def public_key
[@e, n]
end
def private_key
euclid_algorithm([phi, @e], [phi, 1])
end
private
def phi
(@p - 1) * (@q - 1)
end
def n
@p * @q
end
def euclid_algorithm(arr1, arr2)
return arr2[1] if arr1[1] == 1
# binding.pry
quotient = arr1[0] / arr1[1]
next_1 = arr1[0] - (arr1[1] * quotient)
next_2 = (arr2[0] - (arr2[1] * quotient)) % phi
arr1.push(next_1).shift
arr2.push(next_2).shift
euclid_algorithm(arr1, arr2)
end
def generate_primes
a = rand(10..99)
b = rand(10..99)
rand(10..99).times do
a = a.next_prime
end
rand(10..99).times do
b = b.next_prime
end
return [a, b] if coprime?((a - 1) * (b - 1), @e)
generate_primes
end
def gcd(a, b)
return a if b == 0
gcd(b, a % b)
end
def coprime?(a, b)
gcd(a, b) == 1
end
end
# Person class to send and receive messages
class Person
attr_reader :name
def initialize(name)
@name = name
@rsa = RSA.new
@keychain = { self: public_key }
end
def send_message(recipient, message)
recipient.receive_message(message)
end
def send_encrypted(recipient, message)
if @keychain[recipient]
p crypt = @rsa.encrypt(@keychain[recipient], message)
send_message recipient, crypt
else
p "#{@name} doesn't have a public key for #{recipient.name}.'
p 'Cannot send an encrypted message"
end
end
def send_public_key(recipient)
recipient.add_public_key(self, public_key)
end
def receive_message(message)
message = @rsa.decrypt message if message.class == Array
message
end
def public_key
@rsa.public_key
end
def add_public_key(person, key)
@keychain[person] = key
end
end
alice = Person.new('Alice')
bob = Person.new('Bob')
p 'Alice is sending an unencrypted message to bob'
p "The message says 'Hello'"
alice.send_message(bob, 'Hello')
p 'Bob wants to Alice to send encrypted messages in the future.'
p 'Bob sends alice his public key so messages can be encrypted'
p bob.public_key
bob.send_public_key(alice)
p 'Alice is going to send an encrypted message using the public key from bob'
alice.send_encrypted(bob, 'This is an encrypted')
p 'Now you can\'t eavesdrop on alice and bob'
p 'Charlie wants to intercept Alice\'s message to Bob'
p 'Using Bob\'s public key, Charlie can attempt to crack his private key'
p "Bob\'s public key has an exponent, e = #{bob.public_key[0]}"
p "and modulus n = #{bob.public_key[1]}"
p 'Charlie attempts to factorize the modulus, n'
p factors = (bob.public_key[1]).prime_factors
p 'Now charlie can derive Bob\'s private key the same way Bob himself did'
rsa = RSA.new(factors)
p "Bob's private key is #{rsa.private_key}"
p 'Bob should use larger prime numbers next time'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment