Last active
December 13, 2015 01:16
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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