encryptor 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dc166257be860dc17bd32f793762a69aa8d0bb17
4
+ data.tar.gz: 0229093567b3307695cb5cf2bdd12de1005cc32d
5
+ SHA512:
6
+ metadata.gz: 48dea5301f97036ce8198865090fb7ae13e8385ec1376332043a06ec25d9ae17dfb49d629fc962a193faa763f3cf9624252609b59149a3fba4fe6ead7230b7f1
7
+ data.tar.gz: 554b9fd4f471a8ef2131b0bce5c2fa843f0abe277f54bbe1a9d6c28e156aa56d0d23077be08c8f003a2d396f9c14347419bc41fc7026d6c2d9a46d8408977058
Binary file
@@ -0,0 +1 @@
1
+ ��A�\D|��<�2bU� �PK���Ot#g'n7����˜���$Qu��zʎ%���W&�9ƶ�Ե�ϝv�0�m�0�HL(��PW�~sC�6e@
@@ -0,0 +1,5 @@
1
+ pkg
2
+ rdoc
3
+ .bundle
4
+ coverage
5
+ Gemfile.lock
@@ -0,0 +1,19 @@
1
+ sudo: false
2
+ language: ruby
3
+ cache: bundler
4
+ matrix:
5
+ fast_finish: true
6
+ include:
7
+ - rvm: 2.0.0
8
+ - rvm: 2.1
9
+ - rvm: 2.2
10
+ - rvm: 2.3.0
11
+ - rvm: jruby
12
+ - rvm: rbx
13
+ allow_failures:
14
+ - rvm: jruby
15
+ exclude:
16
+ - rvm: 1.9.3
17
+ addons:
18
+ code_climate:
19
+ repo_token: 5dcb75d5b6c58e2a5f6dc850eb2c1d4e0dbf262e69981db00b765a66bfc9ef10
@@ -0,0 +1,24 @@
1
+ # Encryptor #
2
+
3
+ ## Unreleased ##
4
+
5
+ * Added support for MRI 2.1, 2.2, 2.3, and Rubinius. (@saghaulor)
6
+ * Added support for Authenticated Encryption Authentiation Data (AEAD) via aes-###-gcm. (@saghaulor)
7
+ * Changed the defaults to improve security, aes-256-gcm, IV is required. (@saghaulor)
8
+ * Added key and IV minimum length validations. (@saghaulor)
9
+ * Added insecure_mode option to allow for backwards compatibility for users who didn't use unique IV. (@saghaulor)
10
+ * Deprecated using Encryptor without an IV.
11
+ * Added hmac_iterations option to allow for adjusting the number of PKCS5 iterations when deriving a unique key. (@saghaulor)
12
+ * Removed support for MRI 1.9.3 and JRuby (until JRuby supports `auth_data=`, https://github.com/jruby/jruby/issues/3376). (@saghaulor)
13
+ * Changed tests to use Minitest. (@saghaulor)
14
+ * Changed syntax to use Ruby 1.9+ hash syntax. (@saghaulor)
15
+ * Salt may be deprecated in a future release, it remains for backwards compatibility. It's better security to have a unique key per record, however, the cost of PKCS5 is too high to force on to users by default. If users want a unique key per record they can implement it in their own way.
16
+
17
+ ## 1.3.0 ##
18
+
19
+ * Added support for unique key (via salt) and IV. (@danpal & @rcook)
20
+
21
+ ## 1.2.3 ##
22
+
23
+ * Added support for passing blocks to `encrypt` and `decrypt`. (@shuber)
24
+ * Changed raising an exception if key is missing or empty. (@shuber)
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- ## Encryptor [![Build Status](https://travis-ci.org/attr-encrypted/encryptor.png?branch=master)](https://travis-ci.org/attr-encrypted/encryptor)
1
+ # Encryptor
2
2
 
3
- A simple wrapper for the standard Ruby OpenSSL library
3
+ [![Build Status](https://secure.travis-ci.org/attr-encrypted/encryptor.svg)](https://travis-ci.org/attr-encrypted/encryptor) [![Code Climate](https://codeclimate.com/github/attr-encrypted/encryptor/badges/gpa.svg)](https://codeclimate.com/github/attr-encrypted/encryptor) [![Coverage](https://codeclimate.com/github/attr-encrypted/encryptor/badges/coverage.svg)](https://codeclimate.com/github/attr-encrypted/encryptor) [![Gem Version](https://badge.fury.io/rb/encryptor.svg)](http://badge.fury.io/rb/encryptor) [![security](https://hakiri.io/github/attr-encrypted/encryptor/master.svg)](https://hakiri.io/github/attr-encrypted/encryptor/master)
4
4
 
5
- Intended to be used by a future version of `http://github.com/shuber/attr_encrypted` to easily encrypt/decrypt attributes in any Ruby class or model.
5
+ A simple wrapper for the standard Ruby OpenSSL library
6
6
 
7
7
  ### Installation
8
8
 
@@ -14,104 +14,199 @@ gem install encryptor
14
14
 
15
15
  #### Basic
16
16
 
17
- Encryptor uses the AES-256-CBC algorithm by default to encrypt strings securely. You are strongly advised to use both an initialization vector (via the `:iv` option) and a salt (via the `:salt` option) to perform this encryption as securely as possible. Specifying only an `:iv` option without `:salt` is not recommended but is supported as part of a "compatibility mode" to support clients built using older versions of this gem.
17
+ Encryptor uses the AES-256-GCM algorithm by default to encrypt strings securely.
18
18
 
19
19
  The best example is:
20
20
 
21
21
  ```ruby
22
- salt = Time.now.to_i.to_s
23
- secret_key = 'secret'
24
- iv = OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv
25
- encrypted_value = Encryptor.encrypt('some string to encrypt', :key => secret_key, :iv => iv, :salt => salt)
26
- decrypted_value = Encryptor.decrypt(encrypted_value, :key => secret_key, :iv => iv, :salt => salt)
22
+ cipher = OpenSSL::Cipher.new('aes-256-gcm')
23
+ cipher.encrypt # Required before '#random_key' or '#random_iv' can be called. http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-encrypt
24
+ secret_key = cipher.random_key # Insures that the key is the correct length respective to the algorithm used.
25
+ iv = cipher.random_iv # Insures that the IV is the correct length respective to the algorithm used.
26
+ salt = SecureRandom.random_bytes(16)
27
+ encrypted_value = Encryptor.encrypt(value: 'some string to encrypt', key: secret_key, iv: iv, salt: salt)
28
+ decrypted_value = Encryptor.decrypt(value: encrypted_value, key: secret_key, iv: iv, salt: salt)
27
29
  ```
28
30
 
29
- The value to encrypt or decrypt may also be passed as the :value option if you'd prefer.
31
+ A slightly easier example is:
32
+
33
+ ```ruby
34
+ require 'securerandom'
35
+ secret_key = SecureRandom.random_bytes(32) # The length in bytes must be equal to or greater than the algorithm bit length.
36
+ iv = SecureRandom.random_bytes(12) # Recomended length for AES-###-GCM algorithm. https://tools.ietf.org/html/rfc5084#section-3.2
37
+ encrypted_value = Encryptor.encrypt(value: 'some string to encrypt', key: secret_key, iv: iv)
38
+ decrypted_value = Encryptor.decrypt(value: encrypted_value, key: secret_key, iv: iv)
39
+ ```
40
+
41
+ **NOTE: It is imperative that you use a unique IV per each string and encryption key combo; a nonce as the IV.**
42
+ See [RFC 5084](https://tools.ietf.org/html/rfc5084#section-1.5) for more details.
43
+
44
+ The value to encrypt or decrypt may also be passed as the first option if you'd prefer.
30
45
 
31
46
  ```ruby
32
- encrypted_value = Encryptor.encrypt(:value => 'some string to encrypt', :key => secret_key, :iv => iv, :salt => salt)
33
- decrypted_value = Encryptor.decrypt(:value => encrypted_value, :key => secret_key, :iv => iv, :salt => salt)
47
+ encrypted_value = Encryptor.encrypt('some string to encrypt', key: secret_key, iv: iv)
48
+ decrypted_value = Encryptor.decrypt(encrypted_value, key: secret_key, iv: iv)
34
49
  ```
35
50
 
36
- **You may also skip the salt and the IV if you like. Do so at your own risk!**
51
+ #### Options
52
+
53
+ **Defaults:**
37
54
 
38
55
  ```ruby
39
- encrypted_value = Encryptor.encrypt(:value => 'some string to encrypt', :key => 'secret')
40
- decrypted_value = Encryptor.decrypt(:value => encrypted_value, :key => 'secret')
56
+ { algorithm: 'aes-256-gcm',
57
+ auth_data: '',
58
+ insecure_mode: false,
59
+ hmac_iterations: 2000 }
41
60
  ```
42
61
 
43
- You may also pass an `:algorithm` option, though this is not required.
62
+ 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.
63
+
64
+ 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.
44
65
 
45
66
  ```ruby
46
- Encryptor.default_options.merge!(:algorithm => 'aes-128-cbc', :key => 'some default secret key', :iv => iv, :salt => salt)
67
+ Encryptor.default_options.merge!(algorithm: 'aes-256-cbc', key: 'some default secret key', iv: iv, salt: salt)
47
68
  ```
48
69
 
49
70
  #### Strings
50
71
 
51
- Encryptor adds `encrypt` and `decrypt` methods to `String` objects for your convenience. These two methods accept the same arguments as the associated ones in the `Encryptor` module. They're nice when you set the default options in the `Encryptor.default_options attribute.` For example:
72
+ Older versions of Encryptor added `encrypt` and `decrypt` methods to `String` objects for your convenience. However, this behavior has been removed to avoid polluting Ruby's core `String` class. The `Encryptor::String` module remains within this gem to allow users of this feature to implement it themselves. These `encrypt` and `decrypt` methods accept the same arguments as the associated ones in the `Encryptor` module. They're nice when you set the default options in the `Encryptor.default_options attribute.` For example:
52
73
 
53
74
  ```ruby
54
- Encryptor.default_options.merge!(:key => 'some default secret key', :iv => iv, :salt => salt)
75
+ require 'encryptor/string'
76
+ String.include Encryptor::String
77
+ Encryptor.default_options.merge!(key: 'some default secret key', iv: iv)
55
78
  credit_card = 'xxxx xxxx xxxx 1234'
56
79
  encrypted_credit_card = credit_card.encrypt
57
80
  ```
58
81
 
59
82
  There's also `encrypt!` and `decrypt!` methods that replace the contents of a string with the encrypted or decrypted version of itself.
60
83
 
61
- ### Algorithms
62
-
63
- Run `openssl list-cipher-commands` in your terminal to view a list of all cipher algorithms that are supported on your platform. Typically, this will include the following:
64
-
65
- aes-128-cbc
66
- aes-128-ecb
67
- aes-192-cbc
68
- aes-192-ecb
69
- aes-256-cbc
70
- aes-256-ecb
71
- bf
72
- bf-cbc
73
- bf-cfb
74
- bf-ecb
75
- bf-ofb
76
- cast
77
- cast-cbc
78
- cast5-cbc
79
- cast5-cfb
80
- cast5-ecb
81
- cast5-ofb
82
- des
83
- des-cbc
84
- des-cfb
85
- des-ecb
86
- des-ede
87
- des-ede-cbc
88
- des-ede-cfb
89
- des-ede-ofb
90
- des-ede3
91
- des-ede3-cbc
92
- des-ede3-cfb
93
- des-ede3-ofb
94
- des-ofb
95
- des3
96
- desx
97
- idea
98
- idea-cbc
99
- idea-cfb
100
- idea-ecb
101
- idea-ofb
102
- rc2
103
- rc2-40-cbc
104
- rc2-64-cbc
105
- rc2-cbc
106
- rc2-cfb
107
- rc2-ecb
108
- rc2-ofb
109
- rc4
110
- rc4-40
111
-
112
- Note that some ciphers may not be supported by Ruby.
113
-
114
- ### Notes on patches/pull requests
84
+ #### Algorithms
85
+
86
+ To view a list of all cipher algorithms that are supported on your platform, run the following code in your favorite Ruby REPL:
87
+
88
+ ```ruby
89
+ require 'openssl'
90
+ puts OpenSSL::Cipher.ciphers
91
+ ```
92
+
93
+ The supported ciphers will vary depending on the version of OpenSSL that was used to compile your version of Ruby. However, the following ciphers are typically supported:
94
+
95
+ Cipher Name|Key size in bytes|IV size in bytes
96
+ ---|---|---
97
+ aes-128-cbc|16|16
98
+ aes-128-cbc-hmac-sha1|16|16
99
+ aes-128-cbc-hmac-sha256|16|16
100
+ aes-128-ccm|16|12
101
+ aes-128-cfb|16|16
102
+ aes-128-cfb1|16|16
103
+ aes-128-cfb8|16|16
104
+ aes-128-ctr|16|16
105
+ aes-128-ecb|16|0
106
+ aes-128-gcm|16|12
107
+ aes-128-ofb|16|16
108
+ aes-128-xts|32|16
109
+ aes-192-cbc|24|16
110
+ aes-192-ccm|24|12
111
+ aes-192-cfb|24|16
112
+ aes-192-cfb1|24|16
113
+ aes-192-cfb8|24|16
114
+ aes-192-ctr|24|16
115
+ aes-192-ecb|24|0
116
+ aes-192-gcm|24|12
117
+ aes-192-ofb|24|16
118
+ aes-256-cbc|32|16
119
+ aes-256-cbc-hmac-sha1|32|16
120
+ aes-256-cbc-hmac-sha256|32|16
121
+ aes-256-ccm|32|12
122
+ aes-256-cfb|32|16
123
+ aes-256-cfb1|32|16
124
+ aes-256-cfb8|32|16
125
+ aes-256-ctr|32|16
126
+ aes-256-ecb|32|0
127
+ aes-256-gcm|32|12
128
+ aes-256-ofb|32|16
129
+ aes-256-xts|64|16
130
+ aes128|16|16
131
+ aes192|24|16
132
+ aes256|32|16
133
+ bf|16|8
134
+ bf-cbc|16|8
135
+ bf-cfb|16|8
136
+ bf-ecb|16|0
137
+ bf-ofb|16|8
138
+ blowfish|16|8
139
+ camellia-128-cbc|16|16
140
+ camellia-128-cfb|16|16
141
+ camellia-128-cfb1|16|16
142
+ camellia-128-cfb8|16|16
143
+ camellia-128-ecb|16|0
144
+ camellia-128-ofb|16|16
145
+ camellia-192-cbc|24|16
146
+ camellia-192-cfb|24|16
147
+ camellia-192-cfb1|24|16
148
+ camellia-192-cfb8|24|16
149
+ camellia-192-ecb|24|0
150
+ camellia-192-ofb|24|16
151
+ camellia-256-cbc|32|16
152
+ camellia-256-cfb|32|16
153
+ camellia-256-cfb1|32|16
154
+ camellia-256-cfb8|32|16
155
+ camellia-256-ecb|32|0
156
+ camellia-256-ofb|32|16
157
+ camellia128|16|16
158
+ camellia192|24|16
159
+ camellia256|32|16
160
+ cast|16|8
161
+ cast-cbc|16|8
162
+ cast5-cbc|16|8
163
+ cast5-cfb|16|8
164
+ cast5-ecb|16|0
165
+ cast5-ofb|16|8
166
+ des|8|8
167
+ des-cbc|8|8
168
+ des-cfb|8|8
169
+ des-cfb1|8|8
170
+ des-cfb8|8|8
171
+ des-ecb|8|0
172
+ des-ede|16|0
173
+ des-ede-cbc|16|8
174
+ des-ede-cfb|16|8
175
+ des-ede-ofb|16|8
176
+ des-ede3|24|0
177
+ des-ede3-cbc|24|8
178
+ des-ede3-cfb|24|8
179
+ des-ede3-cfb1|24|8
180
+ des-ede3-cfb8|24|8
181
+ des-ede3-ofb|24|8
182
+ des-ofb|8|8
183
+ des3|24|8
184
+ desx|24|8
185
+ desx-cbc|24|8
186
+ idea|16|8
187
+ idea-cbc|16|8
188
+ idea-cfb|16|8
189
+ idea-ecb|16|0
190
+ idea-ofb|16|8
191
+ rc2|16|8
192
+ rc2-40-cbc|5|8
193
+ rc2-64-cbc|8|8
194
+ rc2-cbc|16|8
195
+ rc2-cfb|16|8
196
+ rc2-ecb|16|0
197
+ rc2-ofb|16|8
198
+ rc4|16|0
199
+ rc4-40|5|0
200
+ rc4-hmac-md5|16|0
201
+ seed|16|16
202
+ seed-cbc|16|16
203
+ seed-cfb|16|16
204
+ seed-ecb|16|0
205
+ seed-ofb|16|16
206
+
207
+ **NOTE: Some ciphers may not be supported by Ruby. Additionally, Ruby compiled with OpenSSL >= v1.0.1 will include AEAD ciphers, ie., aes-256-gcm.**
208
+
209
+ #### Notes on patches/pull requests
115
210
 
116
211
  * Fork the project.
117
212
  * Make your feature addition or bug fix.
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'rake'
2
2
  require 'rake/testtask'
3
- require 'rake/rdoctask'
3
+ require 'rdoc/task'
4
+ require "bundler/gem_tasks"
5
+
4
6
 
5
7
  desc 'Test the encryptor gem'
6
8
  Rake::TestTask.new(:test) do |t|
@@ -18,16 +20,5 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
18
20
  rdoc.rdoc_files.include('lib/**/*.rb')
19
21
  end
20
22
 
21
- if RUBY_VERSION < '1.9.3'
22
- require 'rcov/rcovtask'
23
-
24
- task :rcov do
25
- system "rcov -o coverage/rcov --exclude '^(?!lib)' " + FileList[ 'test/**/*_test.rb' ].join(' ')
26
- end
27
-
28
- desc 'Default: run unit tests under rcov.'
29
- task :default => :rcov
30
- else
31
- desc 'Default: run unit tests.'
32
- task :default => :test
33
- end
23
+ desc 'Default: run unit tests.'
24
+ task default: :test
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDdDCCAlygAwIBAgIBATANBgkqhkiG9w0BAQUFADBAMRIwEAYDVQQDDAlzYWdo
3
+ YXVsb3IxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
4
+ bTAeFw0xNjAxMTEyMjQyMDFaFw0xNzAxMTAyMjQyMDFaMEAxEjAQBgNVBAMMCXNh
5
+ Z2hhdWxvcjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYD
6
+ Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx0xdQYk2GwCpQ1n/
7
+ n2mPVYHLYqU5TAn/82t5kbqBUWjbcj8tHAi41tJ19+fT/hH0dog8JHvho1zmOr71
8
+ ZIqreJQo60TqP6oE9a5HncUpjqbRp7tOmHo9E+mOW1yT4NiXqFf1YINExQKy2XND
9
+ WPQ+T50ZNUsGMfHFWB4NAymejRWXlOEY3bvKW0UHFeNmouP5he51TjoP8uCc9536
10
+ 4AIWVP/zzzjwrFtC7av7nRw4Y+gX2bQjrkK2k2JS0ejiGzKBIEMJejcI2B+t79zT
11
+ kUQq9SFwp2BrKSIy+4kh4CiF20RT/Hfc1MbvTxSIl/bbIxCYEOhmtHExHi0CoCWs
12
+ YCGCXQIDAQABo3kwdzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU
13
+ SCpVzSBvYbO6B3oT3n3RCZmurG8wHgYDVR0RBBcwFYETc2FnaGF1bG9yQGdtYWls
14
+ LmNvbTAeBgNVHRIEFzAVgRNzYWdoYXVsb3JAZ21haWwuY29tMA0GCSqGSIb3DQEB
15
+ BQUAA4IBAQAeiGdC3e0WiZpm0cF/b7JC6hJYXC9Yv9VsRAWD9ROsLjFKwOhmonnc
16
+ +l/QrmoTjMakYXBCai/Ca3L+k5eRrKilgyITILsmmFxK8sqPJXUw2Jmwk/dAky6x
17
+ hHKVZAofT1OrOOPJ2USoZyhR/VI8epLaD5wUmkVDNqtZWviW+dtRa55aPYjRw5Pj
18
+ wuj9nybhZr+BbEbmZE//2nbfkM4hCuMtxxxilPrJ22aYNmeWU0wsPpDyhPYxOUgU
19
+ ZjeLmnSDiwL6doiP5IiwALH/dcHU67ck3NGf6XyqNwQrrmtPY0mv1WVVL4Uh+vYE
20
+ kHoFzE2no0BfBg78Re8fY69P5yES5ncC
21
+ -----END CERTIFICATE-----
@@ -0,0 +1,41 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ lib = File.expand_path('../lib/', __FILE__)
4
+ $:.unshift lib unless $:.include?(lib)
5
+
6
+ require 'encryptor/version'
7
+ require 'date'
8
+
9
+ Gem::Specification.new do |s|
10
+ s.name = 'encryptor'
11
+ s.version = Encryptor::Version
12
+ s.date = Date.today
13
+ s.platform = Gem::Platform::RUBY
14
+
15
+ s.summary = 'A simple wrapper for the standard ruby OpenSSL library'
16
+ s.description = 'A simple wrapper for the standard ruby OpenSSL library to encrypt and decrypt strings'
17
+
18
+ s.authors = ['Sean Huber', 'S. Brent Faulkner', 'William Monk', 'Stephen Aghaulor']
19
+ s.email = ['sean@shuber.io', 'sbfaulkner@gmail.com', 'billy.monk@gmail.com', 'saghaulor@gmail.com']
20
+ s.homepage = 'http://github.com/attr-encrypted/encryptor'
21
+ s.license = 'MIT'
22
+ s.rdoc_options = %w(--charset=UTF-8 --inline-source --line-numbers --main README.md)
23
+
24
+ s.require_paths = ['lib']
25
+
26
+ s.files = `git ls-files`.split("\n")
27
+ s.test_files = `git ls-files -- test/*`.split("\n")
28
+
29
+ s.required_ruby_version = '>= 2.0.0'
30
+
31
+ s.add_development_dependency('minitest', '>= 0')
32
+ s.add_development_dependency('rake', '>= 0')
33
+ s.add_development_dependency('simplecov', '>= 0')
34
+ s.add_development_dependency('simplecov-rcov', '>= 0')
35
+ s.add_development_dependency('codeclimate-test-reporter', '>= 0')
36
+
37
+ s.requirements << 'openssl, >= v1.0.1'
38
+
39
+ s.cert_chain = ['certs/saghaulor.pem']
40
+ s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
41
+ end
@@ -1,45 +1,48 @@
1
1
  require 'openssl'
2
- require 'encryptor/string'
3
-
4
- String.send(:include, Encryptor::String)
2
+ require 'encryptor/version'
5
3
 
6
4
  # A simple wrapper for the standard OpenSSL library
7
5
  module Encryptor
8
- autoload :Version, 'encryptor/version'
9
6
 
10
7
  extend self
11
8
 
12
9
  # The default options to use when calling the <tt>encrypt</tt> and <tt>decrypt</tt> methods
13
10
  #
14
- # Defaults to { :algorithm => 'aes-256-cbc' }
11
+ # Defaults to { algorithm: 'aes-256-gcm',
12
+ # auth_data: '',
13
+ # insecure_mode: false,
14
+ # hmac_iterations: 2000 }
15
15
  #
16
16
  # Run 'openssl list-cipher-commands' in your terminal to view a list all cipher algorithms that are supported on your platform
17
17
  def default_options
18
- @default_options ||= { :algorithm => 'aes-256-cbc' }
18
+ @default_options ||= { algorithm: 'aes-256-gcm',
19
+ auth_data: '',
20
+ insecure_mode: false,
21
+ hmac_iterations: 2000 }
19
22
  end
20
23
 
21
- # Encrypts a <tt>:value</tt> with a specified <tt>:key</tt>
24
+ # Encrypts a <tt>:value</tt> with a specified <tt>:key</tt> and <tt>:iv</tt>.
22
25
  #
23
- # Optionally accepts <tt>:iv</tt> and <tt>:algorithm</tt> options
26
+ # Optionally accepts <tt>:salt</tt>, <tt>:auth_data</tt>, <tt>:algorithm</tt>, <tt>:hmac_iterations</tt>, and <tt>:insecure_mode</tt> options.
24
27
  #
25
28
  # Example
26
29
  #
27
- # encrypted_value = Encryptor.encrypt(:value => 'some string to encrypt', :key => 'some secret key')
30
+ # encrypted_value = Encryptor.encrypt(value: 'some string to encrypt', key: 'some secret key', iv: 'some unique value', salt: 'another unique value')
28
31
  # # or
29
- # encrypted_value = Encryptor.encrypt('some string to encrypt', :key => 'some secret key')
32
+ # encrypted_value = Encryptor.encrypt('some string to encrypt', key: 'some secret key', iv: 'some unique value', salt: 'another unique value')
30
33
  def encrypt(*args, &block)
31
34
  crypt :encrypt, *args, &block
32
35
  end
33
36
 
34
- # Decrypts a <tt>:value</tt> with a specified <tt>:key</tt>
37
+ # Decrypts a <tt>:value</tt> with a specified <tt>:key</tt> and <tt>:iv</tt>.
35
38
  #
36
- # Optionally accepts <tt>:iv</tt> and <tt>:algorithm</tt> options
39
+ # Optionally accepts <tt>:salt</tt>, <tt>:auth_data</tt>, <tt>:algorithm</tt>, <tt>:hmac_iterations</tt>, and <tt>:insecure_mode</tt> options.
37
40
  #
38
41
  # Example
39
42
  #
40
- # decrypted_value = Encryptor.decrypt(:value => 'some encrypted string', :key => 'some secret key')
43
+ # decrypted_value = Encryptor.decrypt(value: 'some encrypted string', key: 'some secret key', iv: 'some unique value', salt: 'another unique value')
41
44
  # # or
42
- # decrypted_value = Encryptor.decrypt('some encrypted string', :key => 'some secret key')
45
+ # decrypted_value = Encryptor.decrypt('some encrypted string', key: 'some secret key', iv: 'some unique value', salt: 'another unique value')
43
46
  def decrypt(*args, &block)
44
47
  crypt :decrypt, *args, &block
45
48
  end
@@ -47,10 +50,15 @@ module Encryptor
47
50
  protected
48
51
 
49
52
  def crypt(cipher_method, *args) #:nodoc:
50
- options = default_options.merge(:value => args.first).merge(args.last.is_a?(Hash) ? args.last : {})
51
- raise ArgumentError.new('must specify a :key') if options[:key].to_s.empty?
52
- cipher = OpenSSL::Cipher::Cipher.new(options[:algorithm])
53
+ options = default_options.merge(value: args.first).merge(args.last.is_a?(Hash) ? args.last : {})
54
+ raise ArgumentError.new('must specify a key') if options[:key].to_s.empty?
55
+ cipher = OpenSSL::Cipher.new(options[:algorithm])
53
56
  cipher.send(cipher_method)
57
+ unless options[:insecure_mode]
58
+ raise ArgumentError.new("key must be #{cipher.key_len} bytes or longer") if options[:key].bytesize < cipher.key_len
59
+ raise ArgumentError.new('must specify an iv') if options[:iv].to_s.empty?
60
+ raise ArgumentError.new("iv must be #{cipher.iv_len} bytes or longer") if options[:iv].bytesize < cipher.iv_len
61
+ end
54
62
  if options[:iv]
55
63
  cipher.iv = options[:iv]
56
64
  if options[:salt].nil?
@@ -63,13 +71,40 @@ module Encryptor
63
71
  # Use an explicit salt (which can be persisted into a database on a
64
72
  # per-column basis, for example). This is the preferred (and more
65
73
  # secure) mode of operation.
66
- cipher.key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(options[:key], options[:salt], 2000, cipher.key_len)
74
+ cipher.key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(options[:key], options[:salt], options[:hmac_iterations], cipher.key_len)
67
75
  end
68
76
  else
77
+ # This is deprecated and needs to be changed.
69
78
  cipher.pkcs5_keyivgen(options[:key])
70
79
  end
71
80
  yield cipher, options if block_given?
72
- result = cipher.update(options[:value])
81
+ value = options[:value]
82
+ if cipher.authenticated?
83
+ if encryption?(cipher_method)
84
+ cipher.auth_data = options[:auth_data]
85
+ else
86
+ value = extract_cipher_text(options[:value])
87
+ cipher.auth_tag = extract_auth_tag(options[:value])
88
+ # auth_data must be set after auth_tag has been set when decrypting
89
+ # See http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-auth_data-3D
90
+ cipher.auth_data = options[:auth_data]
91
+ end
92
+ end
93
+ result = cipher.update(value)
73
94
  result << cipher.final
95
+ result << cipher.auth_tag if cipher.authenticated? && encryption?(cipher_method)
96
+ result
97
+ end
98
+
99
+ def encryption?(cipher_method)
100
+ cipher_method == :encrypt
101
+ end
102
+
103
+ def extract_cipher_text(value)
104
+ value[0..-17]
105
+ end
106
+
107
+ def extract_auth_tag(value)
108
+ value[-16..-1]
74
109
  end
75
110
  end