if __name__ is None
if __name__ is None

Reputation: 11533

Unescaped quote (') breaks Base64 in Ruby

I am trying to AES encrypt some data, and I use random_iv to generate an initialization vector.

def encrypt (data)
    cipher = OpenSSL::Cipher::AES.new(256, :CBC)
    cipher.encrypt
    cipher.key = @key
    iv = cipher.random_iv
    encrypted = cipher.update(data) + cipher.final
    encoded = Base64.encode64(encrypted+iv) 
    return encoded, self.sign(encoded)
end

If iv contains ', the encoded string ends there and everything after ' is ignored. I think random_iv is OpenSSL::Random.random_bytes(16), didn't check source yet. When I get random_iv like:

"0\xC8x'6\x86\x06\xFEG[\xCE\xF5\xF5\xCBL>"

the last 16 characters of the encoded string are nothing like it (end at C8).

Base64.decode64('1JCQvhD6mrDXkhW4Hn9HIIr32TmYlBmp803oxOtrZMg=\n')[-16,16]
# => "\x8A\xF7\xD99\x98\x94\x19\xA9\xF3M\xE8\xC4\xEBkd\xC8" 

If there is no ' in iv, it works fine. Right now, I'm solving this problem like this:

cipher.iv = Digest::MD5.new.digest(cipher.random_iv)

since MD5 is 16byte too, and fits the iv requirement.

Digest::MD5.new.digest('test').bytesize
# => 16 

Wondering if there is any better way to do it. And why is there ' in random_iv in the first place? How should I escape data passed to encode64?

Also, do I need to append bytes to my data until data.bytesize is divisible by 16? Or does Cipher::AES do it for me?

cipher.update(data)

Upvotes: 1

Views: 186

Answers (1)

dbenhur
dbenhur

Reputation: 20408

There's nothing special about ' as the contents of a string. It's just a byte with ASCII value 39.

In the statement encoded = Base64.encode64(encrypted+iv), iv is not the same variable as the cipher.iv you set two lines prior. Do you have a method or accessor in scope named iv? Your base64 string '1JCQvhD6mrDXkhW4Hn9HIIr32TmYlBmp803oxOtrZMg=\n' (which I presume is the result of your function) does not end with the base64 encoding of your example iv, so I'm pretty confident the +iv part of the expression is not seeing "0\xC8x'6\x86\x06\xFEG[\xCE\xF5\xF5\xCBL>".

The base64 encoding of your example iv successfully roundtrips for me:

irb> iv = "0\xC8x'6\x86\x06\xFEG[\xCE\xF5\xF5\xCBL>".force_encoding('BINARY') 
=> "0\xC8x'6\x86\x06\xFEG[\xCE\xF5\xF5\xCBL>"
irb> e = Base64.encode64 iv
=> "MMh4JzaGBv5HW8719ctMPg==\n"
irb> d = Base64.decode64 e
=> "0\xC8x'6\x86\x06\xFEG[\xCE\xF5\xF5\xCBL>"
irb> d == iv
=> true

Upvotes: 3

Related Questions