encryptor: ArgumentError: key must be 32 bytes
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.
About this issue
- Original URL
- State: open
- Created 7 years ago
- Comments: 25 (3 by maintainers)
Commits related to this issue
- Fix bug with key length https://github.com/attr-encrypted/encryptor/issues/26 — committed to sophomoric/secret by AdrianCann 6 years ago
- Fix bug with key length https://github.com/attr-encrypted/encryptor/issues/26 — committed to sophomoric/secret by AdrianCann 6 years ago
- Fix bug with key length https://github.com/attr-encrypted/encryptor/issues/26 ***Temporary*** * This is a temporary fix to make this work and get all the CI tests working. Not the most secure soluti... — committed to sophomoric/secret by AdrianCann 6 years ago
- Added explicit truncation of the IV because of https://github.com/attr-encrypted/encryptor/issues/26. — committed to charl/two_factor_authentication by deleted user 5 years ago
@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 😃.
I don’t think that the issue really comes from
Encryptor
; afaik this is the change in the OpenSSL library that causes the error: https://github.com/ruby/ruby/commit/ce635262f53b760284d56bb1027baebaaec175d1We’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:
Then in
initializers/encryptor.rb
: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 )
I think it’d be a big help to improve this line to warn people about this issue.
The “or longer” is not true in this case.
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 )
@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.
@NullVoxPopuli …
variable
(environment or otherwise)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.
Here’s a way I figured out can generate a 32 character key without using SecureRandom…
Just run this in
rails console
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.
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 😄UPDATE (Sep 26th, 2017):
This gem seems to work now with Rails 5.0.6 and Ruby 2.4.2.
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.
a simple
bundle update
did it for me@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.
@danielricecodes I don’t recommend doing that, but you’re welcome to do whatever you like.