encryptor 1.3.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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