symmetric-encryption 3.7.2 → 3.8.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.
- checksums.yaml +4 -4
- data/README.md +65 -83
- data/Rakefile +4 -4
- data/lib/rails/generators/symmetric_encryption/config/config_generator.rb +3 -3
- data/lib/rails/generators/symmetric_encryption/heroku_config/heroku_config_generator.rb +3 -3
- data/lib/rails/generators/symmetric_encryption/new_keys/new_keys_generator.rb +2 -2
- data/lib/symmetric_encryption.rb +7 -1
- data/lib/symmetric_encryption/cipher.rb +180 -50
- data/lib/symmetric_encryption/coerce.rb +75 -0
- data/lib/symmetric_encryption/config.rb +88 -0
- data/lib/symmetric_encryption/extensions/active_record/base.rb +2 -2
- data/lib/symmetric_encryption/extensions/mongoid/encrypted.rb +2 -2
- data/lib/symmetric_encryption/generator.rb +5 -1
- data/lib/symmetric_encryption/railtie.rb +3 -3
- data/lib/symmetric_encryption/railties/symmetric_encryption.rake +6 -6
- data/lib/symmetric_encryption/railties/symmetric_encryption_validator.rb +1 -1
- data/lib/symmetric_encryption/reader.rb +16 -14
- data/lib/symmetric_encryption/symmetric_encryption.rb +30 -285
- data/lib/symmetric_encryption/version.rb +1 -1
- data/lib/symmetric_encryption/writer.rb +13 -13
- data/test/active_record_test.rb +126 -73
- data/test/cipher_test.rb +42 -42
- data/test/mongo_mapper_test.rb +171 -114
- data/test/mongoid_test.rb +173 -115
- data/test/reader_test.rb +63 -63
- data/test/symmetric_encryption_test.rb +81 -80
- data/test/test_db.sqlite3 +0 -0
- data/test/test_helper.rb +1 -2
- data/test/writer_test.rb +20 -20
- metadata +13 -13
- data/lib/_test_empty +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6bf012ca0a4d3ce067a363f363168e4fcc5bebf9
|
4
|
+
data.tar.gz: 005d3a55560d79f658464ba189957f5349d221e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 243de98a4079b584cd215f81f15caf4e6d6985047804a6e74dbcd46e25dbfd45a7f2a3e5d6610c47309a54b0845f6bc10bbb2d7cdd9ec8ac05e5b81361335647
|
7
|
+
data.tar.gz: 56b65cf943afec131341309628ff39101be17321b194f0136cf593d67030df52b41ea1a91abcc445805c3dd2882c5802bb5a84376e352f6282d1f415342156bc
|
data/README.md
CHANGED
@@ -1,16 +1,18 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# symmetric-encryption
|
2
|
+
   
|
3
3
|
|
4
4
|
* http://github.com/reidmorrison/symmetric-encryption
|
5
5
|
|
6
|
+
Transparently encrypt ActiveRecord, Mongoid, and MongoMapper attributes. Encrypt passwords in configuration files. Encrypt entire files at rest.
|
7
|
+
|
6
8
|
## Introduction
|
7
9
|
|
8
10
|
Any project that wants to meet PCI compliance has to ensure that the data is encrypted
|
9
|
-
whilst in flight and at rest. Amongst many other
|
10
|
-
in configuration files have to be encrypted
|
11
|
+
whilst in flight and at rest. Amongst many other requirements all passwords
|
12
|
+
in configuration files also have to be encrypted.
|
11
13
|
|
12
|
-
|
13
|
-
and consistent way
|
14
|
+
Symmetric Encryption helps achieve compliance by supporting encryption of data in a simple
|
15
|
+
and consistent way.
|
14
16
|
|
15
17
|
Symmetric Encryption uses OpenSSL to encrypt and decrypt data, and can therefore
|
16
18
|
expose all the encryption algorithms supported by OpenSSL.
|
@@ -19,110 +21,90 @@ expose all the encryption algorithms supported by OpenSSL.
|
|
19
21
|
|
20
22
|
For complete documentation see: http://reidmorrison.github.io/symmetric-encryption/
|
21
23
|
|
22
|
-
##
|
24
|
+
## Dependencies
|
23
25
|
|
24
|
-
|
25
|
-
encryption and put the iv in the encrypted data as its header, without having
|
26
|
-
to use SymmetricEncryption::Writer
|
26
|
+
Symmetric Encryption works with the following Ruby interpreters:
|
27
27
|
|
28
|
-
*
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
fields
|
34
|
-
|
35
|
-
* SymmetricEncryption.encrypt has two additional optional parameters:
|
36
|
-
```
|
37
|
-
random_iv [true|false]
|
38
|
-
Whether the encypted value should use a random IV every time the
|
39
|
-
field is encrypted.
|
40
|
-
It is recommended to set this to true where feasible. If the encrypted
|
41
|
-
value could be used as part of a SQL where clause, or as part
|
42
|
-
of any lookup, then it must be false.
|
43
|
-
Setting random_iv to true will result in a different encrypted output for
|
44
|
-
the same input string.
|
45
|
-
Note: Only set to true if the field will never be used as part of
|
46
|
-
the where clause in an SQL query.
|
47
|
-
Note: When random_iv is true it will add a 8 byte header, plus the bytes
|
48
|
-
to store the random IV in every returned encrypted string, prior to the
|
49
|
-
encoding if any.
|
50
|
-
Default: false
|
51
|
-
Highly Recommended where feasible: true
|
52
|
-
|
53
|
-
compress [true|false]
|
54
|
-
Whether to compress str before encryption
|
55
|
-
Should only be used for large strings since compression overhead and
|
56
|
-
the overhead of adding the 'magic' header may exceed any benefits of
|
57
|
-
compression
|
58
|
-
Note: Adds a 6 byte header prior to encoding, only if :random_iv is false
|
59
|
-
Default: false
|
60
|
-
```
|
61
|
-
|
62
|
-
## Upgrading from earlier versions to SymmetricEncryption V3
|
28
|
+
* Ruby 1.9.3, 2.0, 2.1, 2.2, or greater
|
29
|
+
* JRuby 1.7, 9.0.0, or greater
|
30
|
+
* Rubinius 2.5, or greater
|
31
|
+
|
32
|
+
## Upgrading to SymmetricEncryption V3
|
63
33
|
|
64
34
|
In version 3 of SymmetricEncryption, the following changes have been made that
|
65
35
|
may have backward compatibility issues:
|
66
36
|
|
67
|
-
* SymmetricEncryption.decrypt no longer rotates through all the decryption keys
|
37
|
+
* `SymmetricEncryption.decrypt` no longer rotates through all the decryption keys
|
68
38
|
when previous ciphers fail to decrypt the encrypted string.
|
69
39
|
In a very small, yet significant number of cases it was possible to decrypt data
|
70
40
|
using the incorrect key. Clearly the data returned was garbage, but it still
|
71
41
|
returned a string of data instead of throwing an exception.
|
72
|
-
See SymmetricEncryption.select_cipher to supply your own custom logic to
|
42
|
+
See `SymmetricEncryption.select_cipher` to supply your own custom logic to
|
73
43
|
determine the correct cipher to use when the encrypted string does not have a
|
74
44
|
header and multiple ciphers are defined.
|
75
45
|
|
76
|
-
* Configuration file format prior to V1 is no longer supported
|
46
|
+
* Configuration file format prior to V1 is no longer supported.
|
77
47
|
|
78
48
|
* New configuration option has been added to support setting encryption keys
|
79
|
-
from environment variables
|
49
|
+
from environment variables.
|
80
50
|
|
81
|
-
* Cipher.parse_magic_header
|
51
|
+
* `Cipher.parse_magic_header!` now returns a Struct instead of an Array.
|
82
52
|
|
83
|
-
* New config options
|
84
|
-
the encryption key in environment variables
|
53
|
+
* New config options `:encrypted_key` and `:encrypted_iv` to support setting
|
54
|
+
the encryption key in environment variables, or from other sources such as ldap
|
55
|
+
or a central directory service.
|
56
|
+
|
57
|
+
## New features in V1.1 and V2
|
85
58
|
|
86
|
-
|
87
|
-
|
59
|
+
* Ability to randomly generate a new initialization vector (iv) with every
|
60
|
+
encryption and put the iv in the encrypted data as its header, without having
|
61
|
+
to use `SymmetricEncryption::Writer`.
|
88
62
|
|
89
|
-
*
|
90
|
-
|
91
|
-
|
92
|
-
|
63
|
+
* With file encryption randomly generate a new key and initialization vector (iv) with every
|
64
|
+
file encryption and put the key and iv in the encrypted data as its header which
|
65
|
+
is encrypted using the global key and iv.
|
66
|
+
|
67
|
+
* Support for compression.
|
68
|
+
|
69
|
+
* `SymmetricEncryption.encrypt` has two additional optional parameters:
|
70
|
+
* random_iv `[true|false]`
|
71
|
+
* Whether the encypted value should use a random IV every time the
|
72
|
+
field is encrypted.
|
73
|
+
* It is recommended to set this to true where feasible. If the encrypted
|
74
|
+
value could be used as part of a SQL where clause, or as part
|
75
|
+
of any lookup, then it must be false.
|
76
|
+
* Setting random_iv to true will result in a different encrypted output for
|
77
|
+
the same input string.
|
78
|
+
* Note: Only set to true if the field will never be used as part of
|
79
|
+
the where clause in an SQL query.
|
80
|
+
* Note: When random_iv is true it will add a 8 byte header, plus the bytes
|
81
|
+
to store the random IV in every returned encrypted string, prior to the
|
82
|
+
encoding if any.
|
83
|
+
* Note: Adds a 6 byte header prior to encoding, if not already configured
|
84
|
+
to add the header to all encrypted values.
|
85
|
+
* Default: false
|
86
|
+
* Highly Recommended where feasible: true
|
87
|
+
|
88
|
+
* compress [true|false]
|
89
|
+
* Whether to compress prior to encryption.
|
90
|
+
* Should only be used for large strings since compression overhead and
|
91
|
+
the overhead of adding the 'magic' header may exceed any benefits of
|
92
|
+
compression.
|
93
|
+
* Default: false
|
94
|
+
|
95
|
+
## Versioning
|
93
96
|
|
94
97
|
This project uses [Semantic Versioning](http://semver.org/).
|
95
98
|
|
96
|
-
Author
|
97
|
-
------
|
99
|
+
## Author
|
98
100
|
|
99
101
|
[Reid Morrison](https://github.com/reidmorrison)
|
100
102
|
|
101
|
-
Contributors
|
102
|
-
------------
|
103
|
-
|
104
|
-
* [M. Scott Ford](https://github.com/mscottford)
|
105
|
-
* [Adam St. John](https://github.com/astjohn)
|
106
|
-
|
107
|
-
License
|
108
|
-
-------
|
109
|
-
|
110
|
-
Copyright 2012, 2013, 2014 Reid Morrison
|
111
|
-
|
112
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
113
|
-
you may not use this file except in compliance with the License.
|
114
|
-
You may obtain a copy of the License at
|
115
|
-
|
116
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
103
|
+
## Contributors
|
117
104
|
|
118
|
-
|
119
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
120
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
121
|
-
See the License for the specific language governing permissions and
|
122
|
-
limitations under the License.
|
105
|
+
[Contributors](https://github.com/reidmorrison/symmetric-encryption/graphs/contributors)
|
123
106
|
|
124
|
-
Disclaimer
|
125
|
-
----------
|
107
|
+
## Disclaimer
|
126
108
|
|
127
109
|
Although this library has assisted in meeting PCI Compliance and has passed
|
128
110
|
previous PCI audits, it in no way guarantees that PCI Compliance will be
|
data/Rakefile
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
require 'rake/clean'
|
2
2
|
require 'rake/testtask'
|
3
3
|
|
4
|
-
$LOAD_PATH.unshift File.expand_path(
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
5
5
|
require 'symmetric_encryption/version'
|
6
6
|
|
7
7
|
task :gem do
|
8
|
-
system
|
8
|
+
system 'gem build symmetric-encryption.gemspec'
|
9
9
|
end
|
10
10
|
|
11
11
|
task :publish => :gem do
|
12
12
|
system "git tag -a v#{SymmetricEncryption::VERSION} -m 'Tagging #{SymmetricEncryption::VERSION}'"
|
13
|
-
system
|
13
|
+
system 'git push --tags'
|
14
14
|
system "gem push symmetric-encryption-#{SymmetricEncryption::VERSION}.gem"
|
15
15
|
system "rm symmetric-encryption-#{SymmetricEncryption::VERSION}.gem"
|
16
16
|
end
|
17
17
|
|
18
|
-
desc
|
18
|
+
desc 'Run Test Suite'
|
19
19
|
task :test do
|
20
20
|
Rake::TestTask.new(:functional) do |t|
|
21
21
|
t.test_files = FileList['test/*_test.rb']
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module SymmetricEncryption
|
2
2
|
module Generators
|
3
3
|
class ConfigGenerator < Rails::Generators::Base
|
4
|
-
desc
|
4
|
+
desc 'Creates a SymmetricEncryption configuration file at config/symmetric-encryption.yml'
|
5
5
|
|
6
6
|
argument :key_path, type: :string, optional: false
|
7
7
|
|
8
8
|
def self.source_root
|
9
|
-
@_symmetric_encryption_source_root ||= File.expand_path(
|
9
|
+
@_symmetric_encryption_source_root ||= File.expand_path('../templates', __FILE__)
|
10
10
|
end
|
11
11
|
|
12
12
|
def app_name
|
@@ -14,7 +14,7 @@ module SymmetricEncryption
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def create_config_file
|
17
|
-
template 'symmetric-encryption.yml', File.join('config',
|
17
|
+
template 'symmetric-encryption.yml', File.join('config', 'symmetric-encryption.yml')
|
18
18
|
end
|
19
19
|
|
20
20
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module SymmetricEncryption
|
2
2
|
module Generators
|
3
3
|
class HerokuConfigGenerator < Rails::Generators::Base
|
4
|
-
desc
|
4
|
+
desc 'Creates a SymmetricEncryption configuration file at config/symmetric-encryption.yml for use in heroku'
|
5
5
|
|
6
6
|
def self.source_root
|
7
|
-
@_symmetric_encryption_source_root ||= File.expand_path(
|
7
|
+
@_symmetric_encryption_source_root ||= File.expand_path('../templates', __FILE__)
|
8
8
|
end
|
9
9
|
|
10
10
|
def app_name
|
@@ -12,7 +12,7 @@ module SymmetricEncryption
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def create_config_file
|
15
|
-
template 'symmetric-encryption.yml', File.join('config',
|
15
|
+
template 'symmetric-encryption.yml', File.join('config', 'symmetric-encryption.yml')
|
16
16
|
end
|
17
17
|
|
18
18
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module SymmetricEncryption
|
2
2
|
module Generators
|
3
3
|
class NewKeysGenerator < Rails::Generators::Base
|
4
|
-
desc
|
4
|
+
desc 'Generate new Symmetric key and initialization vector based on values in config/symmetric-encryption.yml'
|
5
5
|
|
6
6
|
argument :environment, type: :string, optional: false
|
7
7
|
|
8
8
|
def create_config_file
|
9
|
-
SymmetricEncryption.generate_symmetric_key_files(File.join('config',
|
9
|
+
SymmetricEncryption.generate_symmetric_key_files(File.join('config', 'symmetric-encryption.yml'), environment)
|
10
10
|
end
|
11
11
|
|
12
12
|
end
|
data/lib/symmetric_encryption.rb
CHANGED
@@ -8,15 +8,21 @@ require 'symmetric_encryption/cipher'
|
|
8
8
|
require 'symmetric_encryption/symmetric_encryption'
|
9
9
|
require 'symmetric_encryption/exception'
|
10
10
|
|
11
|
+
#@formatter:off
|
11
12
|
module SymmetricEncryption
|
13
|
+
autoload :Coerce, 'symmetric_encryption/coerce'
|
14
|
+
autoload :Config, 'symmetric_encryption/config'
|
12
15
|
autoload :Reader, 'symmetric_encryption/reader'
|
13
16
|
autoload :Writer, 'symmetric_encryption/writer'
|
14
17
|
autoload :Generator, 'symmetric_encryption/generator'
|
15
18
|
end
|
19
|
+
#@formatter:on
|
16
20
|
|
17
21
|
# Add support for other libraries only if they have already been loaded
|
18
22
|
require 'symmetric_encryption/railtie' if defined?(Rails)
|
19
|
-
|
23
|
+
if defined?(ActiveRecord::Base) && !defined?(AttrEncrypted::Version)
|
24
|
+
require 'symmetric_encryption/extensions/active_record/base'
|
25
|
+
end
|
20
26
|
require 'symmetric_encryption/railties/symmetric_encryption_validator' if defined?(ActiveModel)
|
21
27
|
require 'symmetric_encryption/extensions/mongoid/encrypted' if defined?(Mongoid)
|
22
28
|
require 'symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key' if defined?(MongoMapper)
|
@@ -18,12 +18,18 @@ module SymmetricEncryption
|
|
18
18
|
|
19
19
|
# Defines the Header Structure returned when parsing the header
|
20
20
|
HeaderStruct = Struct.new(
|
21
|
-
|
22
|
-
:
|
23
|
-
|
24
|
-
:
|
25
|
-
|
26
|
-
:
|
21
|
+
# [true|false] Whether the data is compressed, if supplied in the header
|
22
|
+
:compressed,
|
23
|
+
# [String] IV used to encrypt the data, if supplied in the header
|
24
|
+
:iv,
|
25
|
+
# [String] Key used to encrypt the data, if supplied in the header
|
26
|
+
:key,
|
27
|
+
# [String] Name of the cipher used, if supplied in the header
|
28
|
+
:cipher_name,
|
29
|
+
# [Integer] Version of the cipher used, if supplied in the header
|
30
|
+
:version,
|
31
|
+
# [SymmetricEncryption::Cipher] Cipher matching the header, or SymmetricEncryption.cipher(default_version)
|
32
|
+
:decryption_cipher
|
27
33
|
)
|
28
34
|
|
29
35
|
# Generate a new Symmetric Key pair
|
@@ -31,6 +37,10 @@ module SymmetricEncryption
|
|
31
37
|
# Returns a hash containing a new random symmetric_key pair
|
32
38
|
# consisting of a :key and :iv.
|
33
39
|
# The cipher_name is also included for compatibility with the Cipher initializer
|
40
|
+
#
|
41
|
+
# Notes:
|
42
|
+
# * The key _must_ be properly secured
|
43
|
+
# * The iv can be stored in the clear and it is not necessary to encrypt it
|
34
44
|
def self.random_key_pair(cipher_name = 'aes-256-cbc')
|
35
45
|
openssl_cipher = ::OpenSSL::Cipher.new(cipher_name)
|
36
46
|
openssl_cipher.encrypt
|
@@ -42,15 +52,77 @@ module SymmetricEncryption
|
|
42
52
|
}
|
43
53
|
end
|
44
54
|
|
55
|
+
# Generate new randomized keys and generate key and iv files if supplied
|
56
|
+
# Overwrites key files for the current environment
|
57
|
+
# See: #initialize for parameters
|
58
|
+
def self.generate_random_keys(params)
|
59
|
+
environment = params[:environment]
|
60
|
+
private_rsa_key = config[:private_rsa_key]
|
61
|
+
rsa = OpenSSL::PKey::RSA.new(private_rsa_key) if private_rsa_key
|
62
|
+
key_pair = SymmetricEncryption::Cipher.random_key_pair(params[:cipher_name] || 'aes-256-cbc')
|
63
|
+
key = key_pair[:key]
|
64
|
+
iv = key_pair[:iv]
|
65
|
+
|
66
|
+
puts 'Generated new Symmetric Key for encryption'
|
67
|
+
if params.has_key?(:key)
|
68
|
+
puts 'Put this value in your configuration file for :key'
|
69
|
+
p key
|
70
|
+
elsif file_name = config.delete(:key_filename)
|
71
|
+
write_to_file(file_name, key, rsa)
|
72
|
+
puts("Please copy #{file_name} to the other servers in #{environment}.")
|
73
|
+
elsif params.has_key?(:encrypted_key)
|
74
|
+
encrypted_key = encrypt_key(key, rsa)
|
75
|
+
puts 'If running in Heroku, add the environment specific key:'
|
76
|
+
puts "heroku config:add #{environment.upcase}_KEY1=#{encrypted_key}"
|
77
|
+
puts
|
78
|
+
puts 'Otherwise, set the :encrypted_key value to:'
|
79
|
+
puts encrypted_key
|
80
|
+
end
|
81
|
+
|
82
|
+
puts 'Generated new Initialization Vector for encryption'
|
83
|
+
if params.has_key?(:iv)
|
84
|
+
puts 'Put this value in your configuration file for :iv'
|
85
|
+
p iv
|
86
|
+
elsif file_name = config.delete(:iv_filename)
|
87
|
+
write_to_file(file_name, iv, rsa)
|
88
|
+
puts("Please copy #{file_name} to the other servers in #{environment}.")
|
89
|
+
elsif params.has_key?(:encrypted_iv)
|
90
|
+
encrypted_iv = encrypt_key(iv, rsa)
|
91
|
+
puts 'If running in Heroku, add the environment specific key:'
|
92
|
+
puts "heroku config:add #{environment.upcase}_KEY1=#{encrypted_iv}"
|
93
|
+
puts
|
94
|
+
puts 'Otherwise, set the :encrypted_iv value to:'
|
95
|
+
puts encrypted_iv
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
45
99
|
# Create a Symmetric::Key for encryption and decryption purposes
|
46
100
|
#
|
47
101
|
# Parameters:
|
48
102
|
# :key [String]
|
49
103
|
# The Symmetric Key to use for encryption and decryption
|
104
|
+
# Or,
|
105
|
+
# :key_filename
|
106
|
+
# Name of file containing symmetric key encrypted using the public
|
107
|
+
# key from the private_rsa_key
|
108
|
+
# Or,
|
109
|
+
# :encrypted_key
|
110
|
+
# Symmetric key encrypted using the public key from the private_rsa_key
|
111
|
+
# and then Base64 encoded
|
50
112
|
#
|
51
113
|
# :iv [String]
|
52
114
|
# Optional. The Initialization Vector to use with Symmetric Key
|
53
115
|
# Highly Recommended as it is the input into the CBC algorithm
|
116
|
+
# Or,
|
117
|
+
# Note: The following 2 options are deprecated since it is _not_ necessary
|
118
|
+
# to encrypt the initialization vector (IV)
|
119
|
+
# :iv_filename
|
120
|
+
# Name of file containing symmetric key initialization vector
|
121
|
+
# encrypted using the public key from the private_rsa_key
|
122
|
+
# Or,
|
123
|
+
# :encrypted_iv
|
124
|
+
# Initialization vector encrypted using the public key from the private_rsa_key
|
125
|
+
# and then Base64 encoded
|
54
126
|
#
|
55
127
|
# :cipher_name [String]
|
56
128
|
# Optional. Encryption Cipher to use
|
@@ -84,16 +156,37 @@ module SymmetricEncryption
|
|
84
156
|
# Default: false
|
85
157
|
# Recommended: true
|
86
158
|
#
|
159
|
+
# private_rsa_key [String]
|
160
|
+
# RSA Key used to decrypt key and iv as applicable
|
161
|
+
# Mandatory if :key_filename, :encrypted_key, :iv_filename, or :encrypted_iv is supplied
|
87
162
|
def initialize(params={})
|
88
163
|
params = params.dup
|
89
|
-
@key = params.delete(:key)
|
90
|
-
@iv = params.delete(:iv)
|
91
164
|
@cipher_name = params.delete(:cipher_name) || params.delete(:cipher) || 'aes-256-cbc'
|
92
165
|
@version = params.delete(:version)
|
93
166
|
@always_add_header = params.delete(:always_add_header) || false
|
94
167
|
@encoding = (params.delete(:encoding) || :base64).to_sym
|
95
168
|
|
96
|
-
|
169
|
+
# To decrypt encrypted key or iv files
|
170
|
+
private_rsa_key = params.delete(:private_rsa_key)
|
171
|
+
rsa = OpenSSL::PKey::RSA.new(private_rsa_key) if private_rsa_key
|
172
|
+
|
173
|
+
if key = params.delete(:key)
|
174
|
+
@key = key
|
175
|
+
elsif file_name = params.delete(:key_filename)
|
176
|
+
@key = read_from_file(file_name, rsa)
|
177
|
+
elsif encrypted_key = params.delete(:encrypted_key)
|
178
|
+
@key = decrypt_key(encrypted_key, rsa)
|
179
|
+
end
|
180
|
+
|
181
|
+
if iv = params.delete(:iv)
|
182
|
+
@iv = iv
|
183
|
+
elsif file_name = params.delete(:iv_filename)
|
184
|
+
@iv = read_from_file(file_name, rsa)
|
185
|
+
elsif encrypted_iv = params.delete(:encrypted_iv)
|
186
|
+
@iv = decrypt_key(encrypted_iv, rsa)
|
187
|
+
end
|
188
|
+
|
189
|
+
raise(ArgumentError, 'Missing mandatory parameter :key, :key_filename, or :encrypted_key') unless @key
|
97
190
|
raise(ArgumentError, "Invalid Encoding: #{@encoding}") unless ENCODINGS.include?(@encoding)
|
98
191
|
raise(ArgumentError, "Cipher version has a valid range of 0 to 255. #{@version} is too high, or negative") if (@version.to_i > 255) || (@version.to_i < 0)
|
99
192
|
raise(ArgumentError, "SymmetricEncryption::Cipher Invalid options #{params.inspect}") if params.size > 0
|
@@ -189,16 +282,13 @@ module SymmetricEncryption
|
|
189
282
|
case encoding
|
190
283
|
when :base64
|
191
284
|
encoded_string = ::Base64.encode64(binary_string)
|
192
|
-
|
193
|
-
defined?(Encoding) ? encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING) : encoded_string
|
285
|
+
encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
|
194
286
|
when :base64strict
|
195
287
|
encoded_string = ::Base64.encode64(binary_string).gsub(/\n/, '')
|
196
|
-
|
197
|
-
defined?(Encoding) ? encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING) : encoded_string
|
288
|
+
encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
|
198
289
|
when :base16
|
199
290
|
encoded_string = binary_string.to_s.unpack('H*').first
|
200
|
-
|
201
|
-
defined?(Encoding) ? encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING) : encoded_string
|
291
|
+
encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
|
202
292
|
else
|
203
293
|
binary_string
|
204
294
|
end
|
@@ -214,12 +304,10 @@ module SymmetricEncryption
|
|
214
304
|
case encoding
|
215
305
|
when :base64, :base64strict
|
216
306
|
decoded_string = ::Base64.decode64(encoded_string)
|
217
|
-
|
218
|
-
defined?(Encoding) ? decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING) : decoded_string
|
307
|
+
decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING)
|
219
308
|
when :base16
|
220
309
|
decoded_string = [encoded_string].pack('H*')
|
221
|
-
|
222
|
-
defined?(Encoding) ? decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING) : decoded_string
|
310
|
+
decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING)
|
223
311
|
else
|
224
312
|
encoded_string
|
225
313
|
end
|
@@ -277,17 +365,17 @@ module SymmetricEncryption
|
|
277
365
|
# Cipher name it UTF8 text
|
278
366
|
|
279
367
|
# Remove header and extract flags
|
280
|
-
_, flags
|
281
|
-
compressed
|
282
|
-
include_iv
|
283
|
-
include_key
|
284
|
-
include_cipher= (flags & 0b0001_0000_0000_0000) != 0
|
368
|
+
_, flags = buffer.slice!(0..MAGIC_HEADER_SIZE+1).unpack(MAGIC_HEADER_UNPACK)
|
369
|
+
compressed = (flags & 0b1000_0000_0000_0000) != 0
|
370
|
+
include_iv = (flags & 0b0100_0000_0000_0000) != 0
|
371
|
+
include_key = (flags & 0b0010_0000_0000_0000) != 0
|
372
|
+
include_cipher = (flags & 0b0001_0000_0000_0000) != 0
|
285
373
|
# Version of the key to use to decrypt the key if present,
|
286
374
|
# otherwise to decrypt the data following the header
|
287
|
-
version
|
375
|
+
version = flags & 0b0000_0000_1111_1111
|
288
376
|
decryption_cipher = SymmetricEncryption.cipher(version)
|
289
377
|
raise(SymmetricEncryption::CipherError, "Cipher with version:#{version.inspect} not found in any of the configured SymmetricEncryption ciphers") unless decryption_cipher
|
290
|
-
iv, key, cipher_name
|
378
|
+
iv, key, cipher_name = nil
|
291
379
|
|
292
380
|
if include_iv
|
293
381
|
len = buffer.slice!(0..1).unpack('v').first
|
@@ -298,7 +386,7 @@ module SymmetricEncryption
|
|
298
386
|
key = decryption_cipher.binary_decrypt(buffer.slice!(0..len-1), header=false)
|
299
387
|
end
|
300
388
|
if include_cipher
|
301
|
-
len
|
389
|
+
len = buffer.slice!(0..1).unpack('v').first
|
302
390
|
cipher_name = buffer.slice!(0..len-1)
|
303
391
|
end
|
304
392
|
|
@@ -330,14 +418,14 @@ module SymmetricEncryption
|
|
330
418
|
# Ruby V2 named parameters would be perfect here
|
331
419
|
|
332
420
|
# Version number of supplied encryption key, or use the global cipher version if none was supplied
|
333
|
-
flags
|
421
|
+
flags = iv || key ? (SymmetricEncryption.cipher.version || 0) : (version || 0) # Same as 0b0000_0000_0000_0000
|
334
422
|
|
335
423
|
# If the data is to be compressed before being encrypted, set the
|
336
424
|
# compressed bit in the flags word
|
337
|
-
flags
|
338
|
-
flags
|
339
|
-
flags
|
340
|
-
flags
|
425
|
+
flags |= 0b1000_0000_0000_0000 if compressed
|
426
|
+
flags |= 0b0100_0000_0000_0000 if iv
|
427
|
+
flags |= 0b0010_0000_0000_0000 if key
|
428
|
+
flags |= 0b0001_0000_0000_0000 if cipher_name
|
341
429
|
header = "#{MAGIC_HEADER}#{[flags].pack('v')}".force_encoding(SymmetricEncryption::BINARY_ENCODING)
|
342
430
|
if iv
|
343
431
|
header << [iv.length].pack('v')
|
@@ -378,18 +466,19 @@ module SymmetricEncryption
|
|
378
466
|
openssl_cipher = ::OpenSSL::Cipher.new(self.cipher_name)
|
379
467
|
openssl_cipher.encrypt
|
380
468
|
openssl_cipher.key = @key
|
381
|
-
add_header
|
382
|
-
result
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
469
|
+
add_header = always_add_header || random_iv || compress if add_header.nil?
|
470
|
+
result =
|
471
|
+
if add_header
|
472
|
+
# Random iv and compress both add the magic header
|
473
|
+
iv = random_iv ? openssl_cipher.random_iv : @iv
|
474
|
+
openssl_cipher.iv = iv if iv
|
475
|
+
# Set the binary indicator on the header if string is Binary Encoded
|
476
|
+
self.class.build_header(version, compress, random_iv ? iv : nil, nil, nil) +
|
477
|
+
openssl_cipher.update(compress ? Zlib::Deflate.deflate(string) : string)
|
478
|
+
else
|
479
|
+
openssl_cipher.iv = @iv if @iv
|
480
|
+
openssl_cipher.update(string)
|
481
|
+
end
|
393
482
|
result << openssl_cipher.final
|
394
483
|
end
|
395
484
|
|
@@ -429,23 +518,23 @@ module SymmetricEncryption
|
|
429
518
|
return str if str.empty?
|
430
519
|
|
431
520
|
if header || self.class.has_header?(str)
|
432
|
-
str
|
521
|
+
str = str.dup
|
433
522
|
header ||= self.class.parse_header!(str)
|
434
523
|
|
435
524
|
openssl_cipher = ::OpenSSL::Cipher.new(header.cipher_name || self.cipher_name)
|
436
525
|
openssl_cipher.decrypt
|
437
526
|
openssl_cipher.key = header.key || @key
|
438
|
-
iv
|
439
|
-
openssl_cipher.iv
|
440
|
-
result
|
527
|
+
iv = header.iv || @iv
|
528
|
+
openssl_cipher.iv = iv if iv
|
529
|
+
result = openssl_cipher.update(str)
|
441
530
|
result << openssl_cipher.final
|
442
531
|
header.compressed ? Zlib::Inflate.inflate(result) : result
|
443
532
|
else
|
444
533
|
openssl_cipher = ::OpenSSL::Cipher.new(self.cipher_name)
|
445
534
|
openssl_cipher.decrypt
|
446
535
|
openssl_cipher.key = @key
|
447
|
-
openssl_cipher.iv
|
448
|
-
result
|
536
|
+
openssl_cipher.iv = @iv if @iv
|
537
|
+
result = openssl_cipher.update(str)
|
449
538
|
result << openssl_cipher.final
|
450
539
|
end
|
451
540
|
end
|
@@ -458,5 +547,46 @@ module SymmetricEncryption
|
|
458
547
|
private
|
459
548
|
|
460
549
|
attr_reader :key
|
550
|
+
|
551
|
+
# Read the encrypted key from file
|
552
|
+
def read_from_file(file_name, rsa)
|
553
|
+
raise(SymmetricEncryption::ConfigError, 'Missing mandatory config parameter :private_rsa_key when filename key is used') unless rsa
|
554
|
+
begin
|
555
|
+
encrypted_key = File.open(file_name, 'rb') { |f| f.read }
|
556
|
+
rsa.private_decrypt(encrypted_key)
|
557
|
+
rescue Errno::ENOENT
|
558
|
+
puts "\nSymmetric Encryption key file: '#{file_name}' not found or readable."
|
559
|
+
puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
# Save symmetric key after encrypting it with the private RSA key
|
564
|
+
# Backing up existing files if present
|
565
|
+
def write_to_file(file_name, key, rsa)
|
566
|
+
raise(SymmetricEncryption::ConfigError, 'Missing mandatory config parameter :private_rsa_key when filename key is used') unless rsa
|
567
|
+
File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if File.exist?(file_name)
|
568
|
+
File.open(file_name, 'wb') { |file| file.write(rsa.public_encrypt(key)) }
|
569
|
+
end
|
570
|
+
|
571
|
+
# Read the encrypted key from file
|
572
|
+
def decrypt_key(encrypted_key, rsa)
|
573
|
+
raise(SymmetricEncryption::ConfigError, 'Missing mandatory config parameter :private_rsa_key when encrypted key is supplied') unless rsa
|
574
|
+
|
575
|
+
# Decode value first using encoding specified
|
576
|
+
encrypted_key = ::Base64.decode64(encrypted_key)
|
577
|
+
if !encrypted_key || encrypted_key.empty?
|
578
|
+
puts "\nSymmetric Encryption encrypted_key not found."
|
579
|
+
puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
|
580
|
+
else
|
581
|
+
rsa.private_decrypt(encrypted_key)
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
# Returns [String] encrypted form of supplied key
|
586
|
+
def encrypt_key(key, rsa)
|
587
|
+
raise(SymmetricEncryption::ConfigError, 'Missing mandatory config parameter :private_rsa_key when encrypted key is supplied') unless rsa
|
588
|
+
::Base64.encode64(rsa.public_encrypt(key))
|
589
|
+
end
|
590
|
+
|
461
591
|
end
|
462
592
|
end
|