Skip to content

Commit

Permalink
Merge pull request #23 from saghaulor/bump_to_v3
Browse files Browse the repository at this point in the history
Bump to v3
  • Loading branch information
saghaulor committed Mar 27, 2016
2 parents b946c18 + 20240d9 commit b9bb760
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Encryptor #

## 3.0.0 ##

* Fixed: GCM algorithms were not using IV. See https://github.com/attr-encrypted/encryptor/pull/22 for more info (@borama)
* Data previously encrypted with GCM will not be decryptable by default. See the README for info regarding a workaround.
* Added: New option to enable backwards compatibility to allow decryption of data encrypted with AES-*-GCM algorithms from Encryptor v2.0.0. (@saghaulor)

## 2.0.0 ##

* Added support for MRI 2.1, 2.2, 2.3, and Rubinius. (@saghaulor)
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

A simple wrapper for the standard Ruby OpenSSL library

## Upgrading from v2.0.0 to v3.0.0 ##
A bug was discovered in Encryptor 2.0.0 wherein the IV was not being used when using an AES-\*-GCM algorithm. Unfornately fixing this major security issue results in the inability to decrypt records encrypted using an AES-\*-GCM algorithm from Encryptor v2.0.0. While the behavior change is minimal between v2.0.0 and v3.0.0, the change has a significant impact on users that used v2.0.0 and encrypted data using an AES-\*-GCM algorithm, which is the default algorithm for v2.0.0. Consequently, we decided to increment the version with a major bump to help people avoid a confusing situation where some of their data will not decrypt. A new option is available in Encryptor 3.0.0 that allows decryption of data encrypted using an AES-\*-GCM algorithm from Encryptor v2.0.0.

### Installation

```bash
Expand Down Expand Up @@ -56,10 +59,11 @@ decrypted_value = Encryptor.decrypt(encrypted_value, key: secret_key, iv: iv)
{ algorithm: 'aes-256-gcm',
auth_data: '',
insecure_mode: false,
hmac_iterations: 2000 }
hmac_iterations: 2000,
v2_gcm_iv: false }
```

Older versions of Encryptor allowed you to use it in a less secure way. Namely, you were allowed to run Encryptor without an IV, or with a key of insufficient length. Encryptor now requires a key and IV of the correct length respective to the algorithm that you use. However, to maintain backwards compatibility you can run Encryptor with the `:insecure_mode` option.
Older versions of Encryptor allowed you to use it in a less secure way. Namely, you were allowed to run Encryptor without an IV, or with a key of insufficient length. Encryptor now requires a key and IV of the correct length respective to the algorithm that you use. However, to maintain backwards compatibility you can run Encryptor with the `:insecure_mode` option. Additionally, when using AES-\*-GCM algorithms in Encryptor v2.0.0, the IV was set incorrectly and was not used. The `:v2_gcm_iv` option is available to allow Encryptor to set the IV as it was set in Encryptor v2.0.0. This is provided to assist with migrating data that unsafely encrypted using an AES-\*-GCM algorithm from Encryptor v2.0.0.

You may also pass an `:algorithm`,`:salt`, and `hmac_iterations` option, however none of these options are required. If you pass the `:salt` option, a new unique key will be derived from the key that you passed in using PKCS5 with a default of 2000 iterations. You can change the number of PKCS5 iterations with the `hmac_iterations` option. As PKCS5 is slow, it is optional behavior, but it does provide more security to use a unique IV and key for every encryption operation.

Expand Down
5 changes: 5 additions & 0 deletions encryptor.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ Gem::Specification.new do |s|

s.cert_chain = ['certs/saghaulor.pem']
s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/

s.post_install_message = "\n\n\nPlease be aware that Encryptor v2.0.0 had a major security bug when using AES-*-GCM algorithms.\n
By default You will not be able to decrypt data that was previously encrypted using an AES-*-GCM algorithm.\n
Please see the README and https://github.com/attr-encrypted/encryptor/pull/22 for more information.\n\n\n"

end
10 changes: 7 additions & 3 deletions lib/encryptor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ module Encryptor
# Defaults to { algorithm: 'aes-256-gcm',
# auth_data: '',
# insecure_mode: false,
# hmac_iterations: 2000 }
# hmac_iterations: 2000,
# v2_gcm_iv: false }
#
# Run 'openssl list-cipher-commands' in your terminal to view a list all cipher algorithms that are supported on your platform
def default_options
@default_options ||= { algorithm: 'aes-256-gcm',
auth_data: '',
insecure_mode: false,
hmac_iterations: 2000 }
hmac_iterations: 2000,
v2_gcm_iv: false }
end

# Encrypts a <tt>:value</tt> with a specified <tt>:key</tt> and <tt>:iv</tt>.
Expand Down Expand Up @@ -60,6 +62,8 @@ def crypt(cipher_method, *args) #:nodoc:
raise ArgumentError.new("iv must be #{cipher.iv_len} bytes or longer") if options[:iv].bytesize < cipher.iv_len
end
if options[:iv]
# This is here for backwards compatibility for Encryptor v2.0.0.
cipher.iv = options[:iv] if options[:v2_gcm_iv]
if options[:salt].nil?
# Use a non-salted cipher.
# This behaviour is retained for backwards compatibility. This mode
Expand All @@ -72,7 +76,7 @@ def crypt(cipher_method, *args) #:nodoc:
# secure) mode of operation.
cipher.key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(options[:key], options[:salt], options[:hmac_iterations], cipher.key_len)
end
cipher.iv = options[:iv]
cipher.iv = options[:iv] unless options[:v2_gcm_iv]
else
# This is deprecated and needs to be changed.
cipher.pkcs5_keyivgen(options[:key])
Expand Down
2 changes: 1 addition & 1 deletion lib/encryptor/version.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Encryptor
# Contains information about this gem's version
module Version
MAJOR = 2
MAJOR = 3
MINOR = 0
PATCH = 0

Expand Down
25 changes: 25 additions & 0 deletions test/compatibility_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,30 @@ def test_decrypt_with_iv_and_salt
assert_equal 'my-fixed-input', result
end
end

def test_ciphertext_encrypted_with_v2_decrypts_with_v2_gcm_iv_option
result = Encryptor.decrypt(@decoded_options)
assert_equal @decoded_options[:plaintext], result
end

def test_ciphertext_encrypted_with_v2_does_not_decrypt_without_v2_gcm_iv_option
assert_raises OpenSSL::Cipher::CipherError do
@decoded_options.delete(:v2_gcm_iv)
Encryptor.decrypt(@decoded_options)
end
end

def setup
encoded_v2_options = {
plaintext: "9H/D+Sm9qMAHHsmWvEu7LGutbEspL6akB1Qb7pLtH0+YOvB9YhZxVuIpugv9\nB8PXrYFnxO+bSvspPgp4KFm4bA==\n",
value: "JR44j1NhT9WOR9SH1n6xYJMcjcGagbsYtnTtGZIe+BSavKZBR8gOtgAFJSTs\nwqtIhr28O8SC7uQepdEctnclahtNf9Nh1j/Wc76Fxlb81KI=\n",
key: "AquHbz6lrUKowAns+qRdwnfEupSbViADKuBMTe7DUpQ=\n",
iv: "YFQ4l87YMy/qQNc10AvmtQ==\n",
salt: "qy3crVknWZpYEjxr89IHUg==\n",
}

@decoded_options = { algorithm: 'aes-256-gcm' , v2_gcm_iv: true }
encoded_v2_options.each_with_object(@decoded_options) { |(k, v), memo| memo[k] = v.unpack("m").first }
end
end

0 comments on commit b9bb760

Please sign in to comment.