Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArgumentError: key must be 32 bytes #26

Open
darrenboyd opened this issue Jan 13, 2017 · 25 comments
Open

ArgumentError: key must be 32 bytes #26

darrenboyd opened this issue Jan 13, 2017 · 25 comments

Comments

@darrenboyd
Copy link

I'm using encryptor via attr-encrypted in a Rails 5 application. While evaluating an upgrade to Ruby 2.4.0, I came across the following error coming out of this gem. Not sure if this is a bug, or something I'm doing wrong, or some change related to Ruby 2.4.0 that requires some effort on my part.

From Ruby 2.4.0

[7] pry(main)> RUBY_VERSION
=> "2.4.0"
[8] pry(main)> secret_key = SecureRandom.random_bytes(64)
=> "\xF3\xA6\xE9\x91\xFD\x94\xCB\xBDH\xA9|\xDF\x04\xBF\xAC\x13+0\xB5\xAF`[\b\xE6\xEDw\xCDD\x97\x19\"\xD1\xB1\xFB\x8A\x8Cn\x84N\x05\xDCp\x1C\xA0o3\x9D\t\xFA\x1F\xC1\x1C&F\xFC\xB0,\xDB\xBE\xE1\x8E9\xD4\xA6"
[9] pry(main)> iv = SecureRandom.random_bytes(12)
=> "\xD5\x00\xB1Q.>)\xAE\xF0x\xBB\xA1"
[10] pry(main)> encrypted_value = Encryptor.encrypt(value: 'some string to encrypt', key: secret_key, iv: iv)
ArgumentError: key must be 32 bytes
from /home/darren/.gem/ruby/2.4.0/gems/encryptor-3.0.0/lib/encryptor.rb:72:in `key='

Previous behavior from Ruby 2.3.3

[1] pry(main)> RUBY_VERSION
=> "2.3.3"
[2] pry(main)> secret_key = SecureRandom.random_bytes(64)
=> "A\xFD\xBC\xA5\x1A\x8E\xD7\x17W\x00r5\x8CHv|\xA7\xFB6\xB8N\x9Fb\x93\xA4\x9Aw\x8E\bq\xBA\xFC\xEF\xA3\x9E\xE2\xED\xB1\b\xBC\xE3\xDA\xEA\xDB\xF2\xAC0\xDAh\xCE\x88/(\x16\xC9\xDDs9\xD11\xE5\xE9\t\\"
[3] pry(main)> iv = SecureRandom.random_bytes(12)
=> "\xC8\xE3\xBE\x91y\x95sF\xE6\x89\x8C\""
[4] pry(main)> encrypted_value = Encryptor.encrypt(value: 'some string to encrypt', key: secret_key, iv: iv)
=> "\x12\x9A\x81*U\xCFT\x91\xB7;\xAF\xF2I]\x9C@L\xD5\xB8;\x00\x87\xF3\x82yS(r\x90\xC8\x86\xBB\x13\x92\xA83$O"

These are both using encryptor 3.0.0.

@spdawson
Copy link

This is affecting my Rails 5.0.1 application too, following an upgrade to Ruby 2.4

@zuzannast
Copy link

Same issue for me when upgrading to Ruby 2.4.0.

@consti
Copy link

consti commented Jan 25, 2017

I don't think that the issue really comes from Encryptor;
afaik this is the change in the OpenSSL library that causes the error: ruby/ruby@ce63526

We've solved it by generating keys that are the correct size length;
if OpenSSL requires 32 bytes, make sure you generate it with SecureRandom.random_bytes(32).

If you plan to store the keys in a YML file (or similar), you will probably have to resort to using Base64.

We did this:

namespace :openssl do
  desc "Generate Base64 encoded bytes for application.yml"
  task generate: :environment do
    {
      ENCRYPT_SECRET_KEY: 32,
      ENCRYPT_IV: 12,
      ENCRYPT_SALT: 128
    }.each do |k, length|
      value = Base64.encode64(SecureRandom.random_bytes(length)).delete("\n")
      puts "#{k}: '#{value}'"
    end
  end
end

Then in initializers/encryptor.rb:

# frozen_string_literal: true
Encryptor.default_options.merge!(
  algorithm: 'aes-256-gcm',
  key: Base64.decode64(ENV['ENCRYPT_SECRET_KEY']),
  iv: Base64.decode64(ENV['ENCRYPT_IV']),
  salt: Base64.decode64(ENV['ENCRYPT_SALT'])
)

@anaumov
Copy link

anaumov commented Feb 22, 2017

Hi there! Could you tell me how I can migrate from 128 byte key to 32 byte key in production?

@darrenboyd
Copy link
Author

@anaumov We were able to simply truncate our keys from their longer length to 32 bytes. It seems that while the Ruby SSL implementation allowed for keys longer than 32 bytes, it truncated them. The recent change is to raise an error for keys that aren't exactly 32 bytes.

Obviously, you should test this to make sure it's works for you :).

@spdawson
Copy link

A simple key truncation also worked in our case.

@saboyutaka
Copy link

Hi there, I got a error like almost same thing, but iv not key.
Since on openssl is merged, key must be 32 bytes and iv must be 12 bytes in Ruby 2.4.0. before 2.4.0, these are truncated.

Hi there, I also encountered the same kind of error but IV not key.
Since this change of openssl have been merged, in Ruby2.4.0, key must be 32 bytes, IV must be 12 bytes.
I tryed to change iv to 12 bytes. And it works
But I think good to fix the number of bytes of ArgumentError in this gem.

@danielricecodes
Copy link

This is a really frustrating issue. It would be great if someone could post their solution to this problem. I've tried the Base64 encode/decode method to no avail. I also arrived here because I'm using the attr_encrypted gem, not this gem specifically.

@saghaulor
Copy link
Contributor

@danielricecodes people have posted their solution. Use the correct sized key and iv. You can trim the extra bytes and it should work.

However, I'm not certain how the IV was handled in Ruby < 2.4. If it was similarly ignored like the extra bytes in the key, then you should be able to simply trim the IV.

@danielricecodes
Copy link

danielricecodes commented Mar 22, 2017

Here's a way I figured out can generate a 32 character key without using SecureRandom...

Just run this in rails console

  `rake secret`[0..31]

It will spit out a 32 byte string - just without all the random garbage SecureRandom will throw in there. You don't have to Base64 encode that value either.

@saghaulor
Copy link
Contributor

@danielricecodes I don't recommend doing that, but you're welcome to do whatever you like.

@danielricecodes
Copy link

danielricecodes commented Mar 22, 2017

I downgraded my app to Ruby 2.3 as a workaround. IMHO, it appears the attr_encrypted gem does not support Ruby 2.4 right now.

So perhaps an Issue needs to be opened for Ruby 2.4 support for attr_encrypted? The gem should behave the same with Ruby 2.4 as it does with 2.3. I'm not a super expert at Ruby or encryption - otherwise I'd take a crack at it myself.

PS, I was testing with Ruby 2.4, Rails 5.0.2, and attr_encrypted 3.0.3. Downgraded to Ruby 2.3 because downgrading Ruby is better than releasing an app with plaintext social security numbers :-D

UPDATE (Sep 26th, 2017):

This gem seems to work now with Rails 5.0.6 and Ruby 2.4.2.

@saghaulor
Copy link
Contributor

@danielricecodes correct, there isn't official support for Ruby 2.4 yet. People are using it at their own risk. I've started work to support it but I've hit a few issues.

@blimmer
Copy link

blimmer commented May 12, 2017

I think it'd be a big help to improve this line to warn people about this issue.

raise ArgumentError.new("key must be #{cipher.key_len} bytes or longer") if options[:key].bytesize < cipher.key_len

The "or longer" is not true in this case.

@wisetara
Copy link

wisetara commented Jul 12, 2017

I'm still running into this issue with Ruby 2.3.4 and Rails 5.0.1. Moreover, my key length indicates that it is 32 bytes, so I don't understand what is happening. It's very frustrating.

UDPATE/EDIT: Updating in case anyone else should stumble in here searching. My Gemfile.lock had updated to encryptor 3.0.0, even though we don't use the gem directly. When I changed it in my Gemfile.lock back to 1.3.0, things went so much better.

@NullVoxPopuli
Copy link

how do you migrate from a shorter key? (21 bytes)

@darrenboyd
Copy link
Author

@NullVoxPopuli ...

  1. add new columns for the field (encypted value and IV)
  2. generate a new proper length key, but don't use the same variable (environment or otherwise)
  3. write code to write the value into both places, encrypted with old key and with new key
  4. write backfill/migration script to run in production that updates the new columns
  5. deploy to production
  6. run the backfill
  7. double-check your work to make sure it worked
  8. triple-check your work to make sure it worked
  9. remove the old columns and old key
  10. deploy your now fully migrated app

Or, at least that's what I would do. This is a useful procedure to try and do, since it's also how you deal with a possibly exposed secret.

@NullVoxPopuli
Copy link

cool, thanks for the tip!

@westonplatter
Copy link

Not sure if this the "correct" answer, but @consti's suggestion worked for me.

AdrianCann added a commit to sophomoric/secret that referenced this issue Jul 1, 2018
AdrianCann added a commit to sophomoric/secret that referenced this issue Jul 1, 2018
AdrianCann added a commit to sophomoric/secret that referenced this issue Jul 1, 2018
attr-encrypted/encryptor#26

***Temporary***
* This is a temporary fix to make this work and get all the CI tests
working. Not the most secure solution
@pduey
Copy link

pduey commented Nov 26, 2018

My key was 32 chars, but 41 bytes because the key was stored in UTF-8. I was able to convert it, truncate to 32 bytes, then store in yml using base64 as suggested by @consti . I converted it something like:
Base64.encode64 Rails.application.secrets[my_key].bytes[0..31].pack( "c" * 32 )

@pramodshinde
Copy link

I think it'd be a big help to improve this line to warn people about this issue.

raise ArgumentError.new("key must be #{cipher.key_len} bytes or longer") if options[:key].bytesize < cipher.key_len

The "or longer" is not true in this case.

Are we planning to change key_len & iv_len checking logic? Code needs to check exact lengths. Happy to raise a PR if its correct approach.

@mvaragnat
Copy link

I was able to upgrade to Rails 2.5.5, without changing my old keys, using @pduey approach

attr_encrypted :my_attribute, key: ENV['MY_KEY'].bytes[0..31].pack( "c" * 32 )

@mvaragnat
Copy link

@pduey or someone, would you mind explaining the pack( "c" * 32 ) part ? it works but I don't get it

@pduey
Copy link

pduey commented Jun 27, 2019

@mvaragnat

Array#pack converts an integer array to a string. The parameter is a format string. In our case, it's interpreting the first 32 elements of the array as a signed integer and printing it in string form. "c" * 32 is shorthand for "cccccccccccccccccccccccccccccccc". Array#pack also accepts "c*" which says interpret the entire array, which would be safe in our case since we also used bytes[0..31].

Also, String#unpack, the converse of Array#pack, could be used instead of String#bytes.

charl pushed a commit to charl/two_factor_authentication that referenced this issue Aug 8, 2019
@Maxeeezy
Copy link

Maxeeezy commented Jul 9, 2020

a simple bundle update did it for me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests