ruby RSA algorithm works - mostly - ruby

I am implementing my own RSA algorithm in ruby to learn more about the algorithm.
It is almost working, but when I decrypt it a few numbers don't decrypt but most do.
Why would this be?
For the given plaintext:
[ 42, 24, 424, 224, 421, 321]
The ciphertext is:
[1239,1263,1495,1349,208,1878]
Which when decrypted is:
[42,690,424,779,421,321]
This is the problem. Why is it happening?
These values are used to produce the keys (The method call is at the end of the program)
p = 51
q = 37
e = 223
n = 1887
phiN = 1800 (coprime with d)
d = 1687
class RSA
#edited to be concise, such as omitting initialize()
def encrypt(plainText)
index = 0
puts 'encrypt in is ', plainText
plainText.each do |i|
plainText[index] = ((i**(#e)) % #n )
index+=1
end
puts 'ciphertext is '
puts plainText
return plainText
end
def decrypt(cipherText)
puts 'decrypt in is ', cipherText
index = 0
cipherText.each do |i|
cipherText[index] = ((i**(#d)) % #n )
index+=1
end
puts 'plaintext is '
puts cipherText
return cipherText
end
def calcD()
#d=1
begin
s = (#d*#e)% #phiN;
#d+=1
end while not s==1
#d -= 1
#puts 'd is ', #d
end
end # class
message = RSA.new(51,37,223,[ 42, 24, 424, 224, 421, 321])

51 is not a prime.
Since that is one of the assumptions for the RSA algorithm, it should be no surprise that it fails to work.
Since your p is not a prime phi(n)!=(p-1)(q-1).
You could make it work by noticing that phi(51*37)=phi(3*17*37)=(3-1)(17-1)(37-1)=1152 and then calculating a working d=e^-1 (mod phi(n)) = 223^-1 (mod 1152) = 31, but I would recommend just using a prime p instead.

Related

How to return "invalid" instead a sum of an array when input is a string or a float

I want to return an array of sums of multiples of 3 and 5 from 0 to n. And I want to return "invalid" when the input is a string, a float or < 0
def is_multiple_of_3_or_5(n)
if n.class == Integer && n > 0
n % 3 == 0 || n % 5 == 0 ? true : false
else
puts "invalid"
end
end
def sum_of_3_and_5_multiples(n)
if n.class == Integer
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
array_of_multiples_of_3_and_5.inject(0, :+)
end
end
sum_of_3_and_5_multiples(-1)
To get the sums of multiples of 3 and 5 I got this but when I try with -1 that return me 0 instead "invalid", with"string"` that return me an error.
You havent' put any code in your sum_of_3_and_5_multiples method to handle what happens if is_multiple_of_3_or_5 is invalid (or to put it another way, a string). You also don't need to puts 'invalid', as this returns a value of null. Just 'invalid' will do:
def is_multiple_of_3_or_5(n)
if n.class == Integer && n > 0
n % 3 == 0 || n % 5 == 0 ? true : false
else
"invalid"
end
end
def sum_of_3_and_5_multiples(n)
if n.class == Integer
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
return "invalid" if is_multiple_of_3_or_5(i).is_a?(String)
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
array_of_multiples_of_3_and_5.inject(0, :+)
end
end
sum_of_3_and_5_multiples(-1)
=> "invalid"
One could do that as follows.
def sum_of_3_and_5_multiples(n)
case n
when Float, String, -Float::INFINITY...0
return 'invalid'
end
((0..n).step(3).to_a + (0..n).step(5).to_a).uniq
end
sum_of_3_and_5_multiples(11.5)
#=> "invalid"
sum_of_3_and_5_multiples("11")
#=> "invalid"
sum_of_3_and_5_multiples(-340)
#=> "invalid"
sum_of_3_and_5_multiples(15)
#=> [0, 3, 6, 9, 12, 15, 5, 10]
sum_of_3_and_5_multiples(87)
#=> [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45,
# 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87,
# 5, 10, 20, 25, 35, 40, 50, 55, 65, 70, 80, 85]
Alternative verbose option, using a monkey patch to String class and a custom Class, stealing from Cary's answer (https://stackoverflow.com/a/59876202/5239030) but with a three dot Range for excluding the extreme value.
Patching the String class for using methods like this Numeric#integer? and Numeric#positive?. I'd suggest to use Kernel#raise in case of error.
module MyStringPatch
def integer?
false
end
def positive?
false
end
end
String.include MyStringPatch
Writing the custom class
class MyNumber
def initialize(n)
raise 'Invalid' unless n.integer? && n.positive?
#n = n
end
def sum_of_3_and_5_multiples
(((0...#n).step(3).to_a + (0...#n).step(5).to_a).uniq).sum
end
end
Finally using it
n = 32
my_number = MyNumber.new(n)
p my_number.sum_of_3_and_5_multiples
#=> 225
Or ...in initialize': Invalid (RuntimeError) in case of n = "32" or n = -32 or n = 32.0.
You can use something like:
return "invalid" unless n.is_a? Integer || n.positive?
Taking a look at: https://rubystyle.guide/ may help
I've found this ! that worked !
def is_multiple_of_3_or_5(n)
n % 3 == 0 || n % 5 == 0 || n == 0 ? true : false
end
def sum_of_3_and_5_multiples(n)
puts n.class
if n.class == Integer && n >= 0
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
return array_of_multiples_of_3_and_5.inject(0, :+)
end
if n.class != Integer || n < 0
return "invalid"
end
end
thanks for your help that was helpful !

Printing output results to one line in the console

Is there an easy way to have the results listed in a single line? For example,
"The numbers outputted were: 99, 85, 70, 50, 35, 20, -2"
def randomMethod()
rand1 = rand(2)
if rand1 == 1
rand2 = rand(1..25)
puts rand2
else
rand2 = 0
puts rand2
end
rand2
end
x = 99
#prints initial x
puts "x = " + "#{x}"
loop do
x -= randomMethod # decrement x by the value returned by randomMethod
puts "x = #{x}"
break if x <= 0
end
Don't use puts in a loop, it terminates with a newline. Instead, accumulate the values in an array and join them with commas once the set is complete:
x_vals = [99]
x_vals << x_vals.last - randomMethod while x_vals.last > 0
puts "The numbers were: #{x_vals.join(", ")}"
While you're at it, you could really tighten up your random method. I'm changing the name to be more conformant with Ruby norms:
def random_method
outcome = rand(1..25) * rand(2)
puts outcome
outcome
end
and if you don't actually need to print the value being generated each time you can completely lose the temporary variables:
def random_method
rand(1..25) * rand(2)
end

calculate fibonacci sequence position from user inserted value

Below is my full program's code:
GradeAvg = [[59, 'F'], [69, 'D'], [79, 'C'], [89, 'B'], [100, 'A']]
def letter_grade
num = number_grade
_, letter = GradeAvg.find { |n, _| num <= n }
[num, letter]
end
def number_grade
loop do
puts 'Please insert a number between 1 and 100.'
num = gets.to_i
break(num) if (1..100).cover?(num)
end
end
def fib(n)
return n if n < 2
fib(n-1) + fib(n-2)
end
10.times { print letter_grade; (num).each { |n| puts fib(n) }; puts }
It is failing with the below error:
undefined local variable or method `num' for main:Object (NameError)
Why can't I apply my fibonacci sequence calculation on my acceptable user inserted value in variable num?
You're trying to access num which is assigned in letter_grade. You must use the return value of the function call (ie num, letter = letter_grade) to read the result.
It should also be noted that Fixnum#each (ie (num).each) is not a method. 10.times { ... } already makes a loop though, so I think this was just an oversight on your part.
See the bolded section for changes. I also changed your fibonacci function to calculate in linear time (instead of exponential time)
GradeAvg = [[59, 'F'], [69, 'D'], [79, 'C'], [89, 'B'], [100, 'A']]
def number_grade
loop do
print "Please insert a number between 1 and 100."
num = gets.to_i
break(num) if (1..100).cover?(num)
end
end
def letter_grade
num = number_grade
_, letter = GradeAvg.find { |n, _| num <= n }
[num, letter]
end
def fib (n, a = 0, b = 1)
if n == 0 then
a
else
fib n - 1, b, a + b
end
end
10.times do
num, letter = letter_grade
puts letter
puts (fib num)
end
Example program execution
# Please insert a number between 1 and 100. 95
# A
# 31940434634990099905
# Please insert a number between 1 and 100. 87
# B
# 679891637638612258
# Please insert a number between 1 and 100. 77
# C
# 5527939700884757
# Please insert a number between 1 and 100. 66
# D
# 27777890035288
# Please insert a number between 1 and 100. 55
# F
# 139583862445
# Please insert a number between 1 and 100. 10
# F
# 55
# Please insert a number between 1 and 100. ...
# ...
Try like this:
GradeAvg = [[59, 'F'], [69, 'D'], [79, 'C'], [89, 'B'], [100, 'A']]
def letter_grade
#num = number_grade
_, letter = GradeAvg.find { |n, _| #num <= n }
[#num, letter]
end
def number_grade
loop do
puts 'Please insert a number between 1 and 100.'
#num = gets.to_i
break(#num) if (1..100).cover?(#num)
end
end
def fib(n)
return n if n < 2
fib(n-1) + fib(n-2)
end
10.times { print letter_grade; puts fib(#num)}

Code wars: Flap Display with while loops

I'm trying to work through a level 5 kata by using while loops. Essentially the problem is to turn each letter rotors[n] number of times and then move on to the next rotors number until you get an output word.
flap_display(["CAT"],[1,13,27])
should output ["DOG"]
Here's what I have so far
def flap_display(lines, rotors)
stuff = "ABCDEFGHIJKLMNOPQRSTUVWXYZ?!##&()|<>.:=-+*/0123456789"
i = 0
j = 0
new_word = lines
while i < rotors.length
while j < new_word[0].length
new_word[0][j] = stuff[stuff.index(new_word[0][j]) + rotors[i]]
j += 1
end
i += 1
j = 0
end
new_word
end
This technically traverses the stuff string and assigns the right letters. However it fails two important things: it does not skip each letter when it rotates to the correct position (C should stop rotating when it hits D, A when it hits O etc) and it does not account for reaching the end of the stuff list and eventually returns a nil value for stuff[stuff.index(new_word[0][j]) + rotors[i]]. How can I fix these two problems using basic loops and enumerables or maybe a hash?
A fuller statement of the problem is given here. This is one Ruby-like way it could be done.
FLAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ?!##&()|<>.:=-+*/0123456789"
NBR_FLAPS = FLAPS.size
def flap_display(str, rot)
rot_cum = rot.each_with_object([]) { |n,a| a << a.last.to_i + n }
str.gsub(/./) { |c| FLAPS[(c.ord + rot_cum.shift - 65) % NBR_FLAPS] }
end
flap_display("CAT", [1,13,27])
#=> "DOG"
flap_display("DOG", [-1,-13,-27])
#=> "CAT"
flap_display("CAT", [5,37,24])
#=> "H*&"
'A'.ord #=> 65 and rot_cum contains the cumulative values of rot:
arr = [1, 13, 27]
rot_cum = arr.each_with_object([]) { |n,a| a << a.last.to_i + n }
#=> [1, 14, 41]
I've written a.last.to_i rather than a.last to deal with the case where a is empty, so a.last #=> nil, meaning a.last.to_i => nil.to_i => 0. See NilClass#to_i. Those opposed to such trickery could write:
rot_cum = arr.drop(1).each_with_object([arr.first]) { |n,a| a << a.last + n }

How to implement Caesar Cipher in Ruby?

I'm learning Ruby on theOdinProject and I need to build the Caeser Cipher. Here's my code:
def caesar_cipher plaintext, factor
codepoints_array = []
ciphertext = ""
plaintext.split('').each do |letter|
if letter =~ /[^a-zA-Z]/
codepoints_array << letter.bytes.join('').to_i
else
codepoints_array << letter.bytes.join('').to_i + factor
end
end
ciphertext = codepoints_array.pack 'C*'
puts ciphertext
end
caesar_cipher("What a string!", 5)
I bet it's not really "elegant" but the main issue here is that the output should be "Bmfy f xywnsl!" but what do I have is "\mfy f xywnsl!". I've been struggling with this task for a couple of days now, but I still have no idea how to "chain" the alphabet so 'z' becomes 'a' with the factor == 1.
I could check the finished tasks of the other people on theOdinProject but their code usually different/more professional and I tried to get a hint, not the final solution. I'll be really thankful if someone could hint me how to resolve this. Thank you in advance.
Hints
Your code would almost work fine if the ASCII table had only 26 characters.
But W is not w, and after z comes {, not a.
So you first need to apply downcase to your letters, offset the bytecode so that a is 0, and do every calculation modulo 26.
Modified version
def caesar_cipher plaintext, factor
codepoints_array = []
ciphertext = ""
a_codepoint = 'a'.ord
plaintext.split('').each do |letter|
if letter =~ /[^a-zA-Z]/
codepoints_array << letter.bytes.join('').to_i
else
shifted_codepoint = letter.downcase.bytes.join('').to_i + factor
codepoints_array << (shifted_codepoint - a_codepoint) % 26 + a_codepoint
end
end
ciphertext = codepoints_array.pack 'C*'
ciphertext
end
puts caesar_cipher("What a string!", 5) #=> "bmfy f xywnsl!"
Another solution
I wrote a small Ruby script for Vigenere chiper a while ago. Caesar cipher is just a variant of it, with the same factor for every character :
class Integer
# 0 => 'a', 1 => 'b', ..., 25 => 'z', 26 => 'a'
def to_letter
('a'.ord + self % 26).chr
end
end
class String
# 'A' => '0', 'a' => 0, ..., 'z' => 25
def to_code
self.downcase.ord - 'a'.ord
end
end
def caesar_cipher(string, factor)
short_string = string.delete('^A-Za-z')
short_string.each_char.map do |char|
(char.to_code + factor).to_letter
end.join
end
puts caesar_cipher("What a string!", 5) #=> "bmfyfxywnsl"
puts caesar_cipher("bmfyfxywnsl", -5) #=> "whatastring"
With ciphers, it is recommended to remove any punctuation sign or whitespace, because they make it much easier to decode the string with statistical analysis.
Caesar cipher is very weak anyway.

Resources