Ken Ken - 3 months ago 14
Ruby Question

NoMethodError when trying to decrypt data using Blowfish

I'm trying to decrypt some data I'm fetching from an API and getting some weird errors.

Some background

The data I'm fetching was encrypted using Blowfish and then encoded to a base64 string and made available in a JSON string. Here's a sample of what that JSON string looks like

{"payload":"BR0UzF38W4oVB7fjP6WgClqdaMKIYTl661mpneqoXQYIYkBQvjlMQZ+yn...."}


In my Ruby script I'm doing the following:

require 'crypt/blowfish'
require 'base64'

# get json data
response = Net::HTTP.get(URI('http://www.url-to-json.com'))
results = JSON.parse(response)

# decode the base64 results
decoded = Base64.decode64(results['payload'])

# setup blowfish object with key
blowfish = Crypt::Blowfish.new('my_secret_key')

# decrypt the data
puts blowfish.decrypt_string(decoded)


And this is the error that is returned:

/Users/Ken/.rvm/gems/ruby-1.9.3-p327@vs/gems/crypt-2.2.1/lib/crypt/stringxor.rb:4:in `^': undefined method `b' for "java.uti":String (NoMethodError)
from /Users/Ken/.rvm/gems/ruby-1.9.3-p327@vs/gems/crypt-2.2.1/lib/crypt/cbc.rb:62:in `decrypt_stream'
from /Users/Ken/.rvm/gems/ruby-1.9.3-p327@vs/gems/crypt-2.2.1/lib/crypt/cbc.rb:115:in `decrypt_string'
from /Users/Ken/Code/vs/scripts/test.rb:55:in `run'
from init.rb:43:in `<main>'


Do you have any insight on what could be causing that error? I've been debugging it for hours and can't seem to make any progress. My best guess is that it's an encoding issue but when I force the encoding using
force_encoding()
I get the same errors.

And in case you're wondering I'm locked into Ruby version 1.9.3-p327 for this app.

Thanks in advanced for any help!

Answer

The culprit is this b method. It was introduced in Ruby 2.0. As you can see in the documentation it is returning copy of a string with ASCII-8BIT encoding. You can either update ruby version or monkey-patch String class to add this method. It is normally implemented in C, but I think this Ruby implementation will work as well:

class String
  def b
    self.dup.force_encoding("ASCII-8BIT")
  end
end