symmetric-encryption 3.7.2 → 3.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![](https://img.shields.io/gem/v/symmetric-encryption.svg) ![](https://secure.travis-ci.org/reidmorrison/symmetric-encryption.png?branch=master) ![](https://img.shields.io/gem/dt/symmetric-encryption.svg) ![](https://img.shields.io/badge/status-production%20ready-blue.svg)
|
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
|