How do I encrypt/decrypt strings in Ruby via Terminal? - ruby

So I'm having some trouble running my script.
My script has two commands : -e which is for encryption, and -d for decryption. The second ARGV is the key for the asymmetric cryptography and the string that it encrypt/decrypts is just static you will see it says 'Words and Stuff' in the code.
When I run the script it just pops up blank and the command is not ran, and when I do try to run -e for example ruby encryptor.rb -e sup3rS3cretKey it just says Invalid command '-e'; type "help" for a list. error in -e. So it seems its running openssl for some reason because of my require `openssl` statement and it does not operate my commands its seems as my script is not being ran from the terminal. So how do I fix this, and what is the openssl thing that it is doing called?
-Script
require `openssl`
if ARGV[0] == '-e' #Encrypt
if ARGV.length != 2
puts "Please input a key."
exit
end
puts "Encrypting"
key = ARGV[1]
cipher = OpenSSL::Cipher.new('Words and Stuff').encrypt
cipher.key = Digest::SHA1.hexdigest key
s = cipher.update(self) + cipher.final
s.unpack('H*')[0].upcase
puts "Encrypted"
elsif ARGV[0] == '-d' #Decrypt
if ARGV.length != 2
puts "Please input a key."
exit
end
puts "Decrypting"
key = ARGV[1]
cipher = OpenSSL::Cipher.new('Words and Stuff').decrypt
cipher.key = Digest::SHA1.hexdigest key
s = [self]/pack("H*").unpack("C*").pack("c*")
cipher.update(s) + cipher.final
puts "String decrypted."
end

Your initial problem is that you are requiring 'openssl' with backticks so it is trying to call it in the shell. Swap the backticks for normal quotes and you will make it past that line.

irb is very handy for these situations. Just type irb and enter require "openssl" at the prompt. Then you can test things out a line at a time.
Here are a few issues:
Backticks around openssl is hanging the script(try single or double quotes as noted).
"Words and Stuff" is not a supported algorithm(try AES-128-CBC).
cipher.key wants a 16 byte string(I just stuck "sup3rS3cretKeyyz" in there)

Related

How to check if 'ARGV' contains both '-p' and '-c' in optparse

This code is intended to check whether the user entered an option with the command:
require 'optparse'
ARGV << '-h' if ARGV.empty?
options = {}
OptionParser.new do |parser|
parser.banner = "Usage: myruby.rb [options]"
parser.on("-h", "--help", "Help myruby") do | |
puts parser
exit
end
parser.on("-p", "--people PEOPLE", "PPPPPPPPPP") do |v|
options[:pppp] = v
end
parser.on("-c", "--coordinate COORDINATE", "ccccccccc") do |x|
options[:coordinate] = x
end
end.parse!
# Start my program from this line
unless options[:pppp] && options[:coordinate]
puts "Exit OK because missing both (option and argument) p,c"
exit
end
puts "It work if only run myruby.rb -p argument_P -c argument_c"
I just found an error. If the user enters only one but not both required ARGV (-p -c).
I can check and exit from my application, but I want to filter ARGV by exiting to assign ARGV << 'h'.
What is the best way?
updated 1: Added unless case before run my program problem : Worked as
asked, but error when -p or -c missing argument. example : ruby
thiscode.rb -p bababa -c error : rb:17:in `': missing
argument (OptionParser::MissingArgument)
Just explicitly check the presence of both after options are parsed:
unless options[:pppp] && options[:coordinate]
puts USAGE # or do whatever else
exit
end

File.exist? always returns false even when file does exist

I have a program that tries to open a file:
Dir.chdir(File.dirname(__FILE__))
puts "Enter file name: ";
relPath = gets;
absPath = Dir.pwd << "/" << relPath;
if File.exist?(absPath) then
puts "File exists";
file = File.open(absPath, "r");
other code...
else
puts "File does not exist";
end
It always prints "File does not exist" even when the current directory exists and the file also exists. The file and script are in the same directory.
I am running it on Mac OS X Yosemite (10.10.3) and Ruby 2.2.0p0.
I can't explain why (albeit I have strong belief that it's for some whitespace characters) but with this little contribution it works ok.
Dir.chdir(File.dirname(__FILE__))
print "Enter file name:";
relPath = gets.chomp; #intuitively used this, and it wroked fine
absPath = File.expand_path(relPath) #used builtin function expand_path instead of string concatenation
puts absPath
puts File.file?(absPath)
if File.exist?(absPath) then
puts "File exists";
puts File.ctime(absPath) #attempting a dummy operation :)
else
puts "File does not exist";
end
runnning code
$ ls -a anal*
analyzer.rb
$ ruby -v
ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-linux]
ziya#ziya:~/Desktop/code/ruby$ ruby fileexists.rb
Enter file name:analyzer.rb
/home/ziya/Desktop/code/ruby/analyzer.rb #as a result of puts absPath
true #File.file?(absPath) => true
File exists
2015-06-11 12:48:31 +0500
That code has syntax error ("if" doesnt need "then"), and you dont have to put ";" after each line.
try
Dir.chdir(File.dirname(__FILE__))
puts "Enter file name: "
relPath = gets
absPath = "#{Dir.pwd}/#{relPath.chop}"
if File.exist?(absPath)
puts "File exists"
file = File.open(absPath, "r")
else
puts "File does not exist"
end
remember that gets will add a new line character so you will need to do a chomp, and that way to concatenate string won't work on ruby.
Your code is not idiomatic Ruby. I'd write it something like this untested code:
Dir.chdir(File.dirname(__FILE__))
puts 'Enter file name: '
rel_path = gets.chomp
abs_path = File.absolute_path(rel_path)
if File.exist?(abs_path)
puts 'File exists'
File.foreach(abs_path) do |line|
# process the line
end
else
puts 'File does not exist'
end
While Ruby supports the use of ;, they're for use when we absolutely must provide multiple commands on one line. The ONLY time I can think of needing that is when using Ruby to execute single-line commands at the command-line. In normal scripts I've never needed ; between statements.
then is used with if when we're using a single line if expression, however, we have trailing if which removes the need for then. For instance, these accomplish the same thing but the second is idiomatic, shorter, less verbose and easier to read:
if true then a = 1 end
a = 1 if true
See "What is the difference between "if" statements with "then" at the end?" for more information.
Instead of relPath and absPath we use snake_case for variables, so use rel_path and abs_path. It_is_a_readability AndMaintenanceThing.
File.absolute_path(rel_path) is a good way to take the starting directory and return the absolute path given a relative directory.
File.foreach is a very fast way to read a file, faster than slurping it using something like File.read. It is also scalable whereas File.read is not.

Ruby optparse odd behavior with exception handling

I was working on making a CLI ruby tool a bit more robust with error handling of the CLI component of the tool. I'm using optparse and the documentation shows that flags can have mandatory and optional arguments. I'm just seeing some odd/annoying behavior.
For one case where the argument of the flag is mandatory (using the equal sign), I was trying to make it fail but it still worked, it just grabbed the next flag ('-u') as the argument to that flag instead which causes the rest of the parsing to barf in a less than ideal way. I guess this is kind of OK since '-....' can be valid but since I have it using the equal sign for setting the value of the switch, I'd assume that the 'space' style assignment wouldn't work.
op = OptionParser.new do |x|
x.on("-u", "--user=USER", "user flag") do |user| options[:user] = user end
x.on("-d", "--db=DATABASE", "database flag") do |db| options[:db] = db end
end
If I pass the following CLI input:
myprog -u -d mydb positionalarg
Then during parsing, it sets options[:user] to be -d, and options[:db] is nil since it is not encountered and there are 2 positional arguments instead of 1. Obviously this is a user error but I want to catch it and display a real error and not just return an error (in the case of the tool) that only one positional argument should be passed from the following list: ... The true issue is that the -u flag is missing it's required argument so given that optparse has a ArgumentMissing exception, I'd assume that's what .parse! would throw.
However, with a flag that has an optional argument (using the equal sign), it is in fact optional if you do: -a -b=something but this does not work: -a something -b=somethingelse. The only way to force the value is to use the equal sign: -a=something -b=somethingelse. Given the previous behavior with the mandatory argument with an equal sign, I'd wouldn't think would be the case. Example:
op = OptionParser.new do |x|
x.on("-u", "--user[=USER]", "user flag") do |user| options[:user] = user unless user.nil? end
x.on("-d", "--db=DATABASE", "database flag") do |db| options[:db] = db end
end
So with parsing:
myprog -u -d mydb positionalarg
Then options[:user] is nil (ok) and options[:db] is mydb and there is one positional arg left over.
With this parsing:
myprog -u myuser -d mydb positionalarg
Then options[:user] is nil (not ok) and options[:db] is mydb and there are two positional args left over: myuser and positionalarg (not ok). My error checking, again, barfs with the positional arg count. It seems that if with mandatory flag arguments, that if both space and = works, then for optional flag args, it should be the same but this is not the case.
The other issue is that the flags with optional args (using space) are fine except if they are at the end of the command and then takes the positional argument as the flags argument.
Example:
op = OptionParser.new do |x|
x.on("-u", "--user [USER]", "user flag") do |user| options[:user] = user unless user.nil? end
x.on("-d", "--db=DATABASE", "database flag") do |db| options[:db] = db end
end
So with parsing:
myprog -d mydb -u positionalarg
Then options[:db] is mydb (ok) and options[:user] is positionalarg and there are no positional arguments left over. Note that -d mydb works with the space even those I specify it with an equals sign.
Seems a lot of people do ruby CLIs by doing optparse .parse! and take the remaining entries in ARGV as the positional arguments, but I'm thinking it might be better to strip the positional arguments off the far end first before passing ARGV to optparse (except this fails in the event of variable number of positional arguments.
I'm confident I can program around all this but I'd prefer not to if there are ways to do it in optparse that I'm unaware of.
Maybe the best thing would be to avoid flags with optional arguments :), but any advice would be appreciated.
Why do you use the = in the definition of the options? The example in the documentation don't use them.
If I define this MWE as test.rb:
require 'optparse'
puts "\n=Call with #{ARGV.inspect}"
options = {}
op = OptionParser.new do |x|
x.on("-u", "--user [USER]", "user flag") do |user| options[:user] = user unless user.nil? end
x.on("-d", "--db DATABASE", "database flag") do |db| options[:db] = db end
end
op.parse!
puts "Options: #{options.inspect}"
puts "ARGV #{ARGV.inspect}"
And call it with this batfile (Windows, remove the #echo off for Linux):
#echo off
test.rb -h
test.rb -u -d mydb positionalarg
test.rb -u myuser -d mydb positionalarg
test.rb --user=myuser -d mydb positionalarg
I get:
=Call with ["-h"]
Usage: test [options]
-u, --user [USER] user flag
-d, --db DATABASE database flag
=Call with ["-u", "-d", "mydb", "positionalarg"]
Options: {:db=>"mydb"}
ARGV ["positionalarg"]
=Call with ["-u", "myuser", "-d", "mydb", "positionalarg"]
Options: {:user=>"myuser", :db=>"mydb"}
ARGV ["positionalarg"]
=Call with ["--user=myuser", "-d", "mydb", "positionalarg"]
Options: {:user=>"myuser", :db=>"mydb"}
ARGV ["positionalarg"]

OpenSSL Command Line and Ruby OpenSSL library differ when using same parameters to encrypt text

Note: Using OpenSSL for symmetric encryption of text.
I made a Ruby script to test OpenSSL and I found I'm getting different results. The key, iv, and ciphers are identical, so I would expect the results to be identical. But they are not. Here's my script:
require 'openssl'
require 'base64'
key = "00000000000000000000000000000000"
iv = "00000000000000000000000000000000"
### OPENSSL Command Line ###
puts "*** OpenSSL Command Line ***"
print "Encrypted via Command Line: "
string = `printf %s \"Hello\" | openssl enc -aes-128-cbc -K #{key} -iv #{iv} -base64`
puts string
puts string.inspect
print "Decrypted Data is: "
puts `printf %s \"BC2+AQJ6ZQx0al3GXba+EQ==\n\" | openssl enc -d -aes-128-cbc -K #{key} - iv #{iv} -base64`
puts "\n"
### Ruby OpenSSL Library ###
puts "*** OpenSSL Ruby Library ***"
cipher = OpenSSL::Cipher.new('aes-128-cbc').encrypt
cipher.padding = 1
cipher.key = key
cipher.iv = iv
encrypted_data = cipher.update("Hello")
encrypted_data << cipher.final
encrypted_data = Base64.encode64(encrypted_data)
puts "Encrypted via Ruby is: #{encrypted_data}"
puts encrypted_data.inspect
decipher = OpenSSL::Cipher.new('aes-128-cbc').decrypt
decipher.key = key
decipher.iv = iv
data = decipher.update(Base64.decode64(encrypted_data))
data << decipher.final
puts "Decrypted Data: #{data}"
The results are:
*** OpenSSL Command Line ***
Encrypted via Command Line: BC2+AQJ6ZQx0al3GXba+EQ==
"BC2+AQJ6ZQx0al3GXba+EQ==\n"
Decrypted Data is: Hello
*** OpenSSL Ruby Library ***
Encrypted via Ruby is: ZkeNEgsUXi1J7ps6kCQxdQ==
"ZkeNEgsUXi1J7ps6kCQxdQ==\n"
Decrypted Data: Hello
Just a curious result. Any idea what's causing the data to be different?
Just a guess, without knowing Ruby's OpenSSL interface:
You give the keys and initialization vector to the command line OpenSSL in hexadecimal encoding. E.g. your key and initialization vector are 0x000....
I suppose your Ruby library takes the key and initialization vector as binary data, e.g you are actually passing a key and initialization vector consisting of 0x30303030... (assuming ASCII or anything compatible to it) instead of 0x00000....
Pack them to a binary(Hex) sequence will fix it.
Test on my machine(Mac ox 10.11.1 ruby-2.2.3).
cipher.key = ["#{key}"].pack('H*')
cipher.iv = ["#{iv}"].pack('H*')
ruby Packs
decipher.key = ["#{key}"].pack('H*')
decipher.iv = ["#{iv}"].pack('H*')

How do I handle a missing mandatory argument in Ruby OptionParser?

In OptionParser I can make an option mandatory, but if I leave out that value it will take the name of any following option as the value, screwing up the rest of the command line parsing.
Here is a test case that echoes the values of the options:
$ ./test_case.rb --input foo --output bar
output bar
input foo
Now leave out the value for the first option:
$ ./test_case.rb --input --output bar
input --output
Is there some way to prevent it taking another option name as a value?
Thanks!
Here is the test case code:
#!/usr/bin/env ruby
require 'optparse'
files = Hash.new
option_parser = OptionParser.new do |opts|
opts.on('-i', '--input FILENAME', 'Input filename - required') do |filename|
files[:input] = filename
end
opts.on('-o', '--output FILENAME', 'Output filename - required') do |filename|
files[:output] = filename
end
end
begin
option_parser.parse!(ARGV)
rescue OptionParser::ParseError
$stderr.print "Error: " + $! + "\n"
exit
end
files.keys.each do |key|
print "#{key} #{files[key]}\n"
end
What you want to do is not a good idea. What if you really have a file named "--output"? This is a perfectly valid filename on Unix. Every Unix program's option parsing works the way the ruby one is doing, so you shouldn't change it, because then your program will be arbitrarily different from everything else, which is confusing and violates the "principle of least surprise."
The real question is: why are you having this problem in the first place? Perhaps you're running your program from another program, and the parent program is providing a blank filename as the parameter to --input, which makes it see --output as the parameter to --input. You can work around this by always quoting the filenames you pass on the command line:
./test_case.rb --input "" --output "bar"
Then --input will be blank, and that's easy to detect.
Also note that if --input is set to --output (and --output is not a real file) you can just try to open the --input file. If it fails, print a message like:
can't open input file: --output: file not found
And that should make it clear to the user what they did wrong.
try this:
opts.on('-i', '--input FILENAME', 'Input filename - required') do |filename|
files[:input] = filename
end
opts.on('-o', '--output FILENAME', 'Output filename - required') do |filename|
files[:output] = filename
end
opts.on("-h", "--help", "Show this message") do
puts opts
exit
end
begin
ARGV << "-h" if ARGV.size != 2
option_parser.parse!(ARGV)
rescue OptionParser::ParseError
$stderr.print "Error: " + $! + "\n"
exit
end
In this case, the mandatory --output option is missing, so do this after calling parse!:
unless files[:input] && files[:output]
$stderr.puts "Error: you must specify both --input and --output options."
exit 1
end
OK - this works - the regular expression in the on() call allows any string as long as it doesn't start with a '-'
If I don't pass an argument to --input and there is another option downstream then it will take that option key as the argument to --input. (e.g. --input --output). The regexp catches that and then I check the error message. If the argument it reports starts with '-' I output the correct error message, namely that there is a missing argument. Not pretty but it seems to work.
Here is my working test case:
#!/usr/bin/env ruby
require 'optparse'
files = Hash.new
option_parser = OptionParser.new do |opts|
opts.on('-i FILENAME', '--input FILENAME', /\A[^\-]+/, 'Input filename - required') do |filename|
files[:input] = filename
end
opts.on('-o FILENAME', '--output FILENAME', /\A[^\-]+/, 'Output filename - required') do |filename|
files[:output] = filename
end
end
begin
option_parser.parse!(ARGV)
rescue OptionParser::ParseError
if $!.to_s =~ /invalid\s+argument\:\s+(\-\-\S+)\s+\-/
$stderr.print "Error: missing argument: #{$1}\n"
else
$stderr.print "Error: " + $! + "\n"
end
exit
end
files.keys.each do |key|
print "#{key} #{files[key]}\n"
end

Resources