symmetric-encryption 3.8.3 → 3.9.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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/Rakefile +16 -16
  4. data/examples/symmetric-encryption.yml +33 -38
  5. data/lib/rails/generators/symmetric_encryption/config/templates/symmetric-encryption.yml +10 -14
  6. data/lib/rails/generators/symmetric_encryption/heroku_config/templates/symmetric-encryption.yml +28 -25
  7. data/lib/symmetric_encryption.rb +14 -6
  8. data/lib/symmetric_encryption/cipher.rb +151 -130
  9. data/lib/symmetric_encryption/config.rb +0 -1
  10. data/lib/symmetric_encryption/encoder.rb +79 -0
  11. data/lib/symmetric_encryption/extensions/active_record/base.rb +94 -134
  12. data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +3 -89
  13. data/lib/symmetric_encryption/key_encryption_key.rb +32 -0
  14. data/lib/symmetric_encryption/railtie.rb +3 -3
  15. data/lib/symmetric_encryption/symmetric_encryption.rb +41 -8
  16. data/lib/symmetric_encryption/utils/re_encrypt_config_files.rb +82 -0
  17. data/lib/symmetric_encryption/version.rb +1 -1
  18. data/test/active_record_test.rb +149 -140
  19. data/test/cipher_test.rb +98 -6
  20. data/test/config/{mongoid_v5.yml → mongoid.yml} +0 -0
  21. data/test/config/symmetric-encryption.yml +4 -10
  22. data/test/config/test_new.key +2 -2
  23. data/test/encoder_test.rb +61 -0
  24. data/test/mongoid_test.rb +12 -22
  25. data/test/reader_test.rb +16 -11
  26. data/test/symmetric_encryption_test.rb +23 -3
  27. data/test/test_db.sqlite3 +0 -0
  28. data/test/test_helper.rb +2 -16
  29. data/test/writer_test.rb +1 -5
  30. metadata +11 -12
  31. data/test/config/mongoid_v2.yml +0 -6
  32. data/test/config/mongoid_v3.yml +0 -9
  33. data/test/mongo_mapper_test.rb +0 -599
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 788a470ed0ed501adc827789838ad6460fa6070b
4
- data.tar.gz: ba3c6a6865c5127fa409271e477ab2caee65c1a6
3
+ metadata.gz: fad0434ee0b203f85088bc62492c16d2365a284c
4
+ data.tar.gz: c8cd4796787faa48be52155d75e1e312f627b38d
5
5
  SHA512:
6
- metadata.gz: 525c6df5648bbf5af0432eaab63a235e0ac0fbf3a9a503576e3cf89d11a8d074820aeea9017067afcce950d37f9902f3b39620e81c3239f615488316be57ff19
7
- data.tar.gz: ac5ae0631681185ac0c2bbdfdb0fce0b61a803ddaa1410bc19cb77a98be7b1b37a9ef7417b047781c38c69ae6b75369085d654578767143e1d7b7143821ce251
6
+ metadata.gz: 59fe734623957ac4abed7b6b94786153a793947528d6b857612f418c6fa151947076b8802b5fe901bc2a1d0bad02aa79aa6790951b0671d054e4274ea09a29cc
7
+ data.tar.gz: 72270dd87fd4e16616b31e7d173facffc2da383c6f0719c0cc76bb9e353d0b33f1179c419af24f36c4ac108e6279b695202163884c01d0d97fe7f770688223b2
data/README.md CHANGED
@@ -33,8 +33,8 @@ Fully supports Symmetric Encryption to encrypt data in flight and at rest while
33
33
 
34
34
  Symmetric Encryption works with the following Ruby VMs:
35
35
 
36
- - Ruby 2.1, 2.2, 2.3, and above
37
- - JRuby 1.7.23, 9.0.5 and above
36
+ - Ruby 2.1 and higher.
37
+ - JRuby 9.1 and higher.
38
38
 
39
39
  ## Upgrading to SymmetricEncryption V3
40
40
 
data/Rakefile CHANGED
@@ -1,8 +1,9 @@
1
- require 'rake/clean'
2
- require 'rake/testtask'
1
+ # Setup bundler to avoid having to run bundle exec all the time.
2
+ require 'rubygems'
3
+ require 'bundler/setup'
3
4
 
4
- $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
5
- require 'symmetric_encryption/version'
5
+ require 'rake/testtask'
6
+ require_relative 'lib/symmetric_encryption/version'
6
7
 
7
8
  task :gem do
8
9
  system 'gem build symmetric-encryption.gemspec'
@@ -15,17 +16,16 @@ task :publish => :gem do
15
16
  system "rm symmetric-encryption-#{SymmetricEncryption::VERSION}.gem"
16
17
  end
17
18
 
18
- desc 'Run Test Suite'
19
- task :test do
20
- Rake::TestTask.new(:functional) do |t|
21
- t.test_files = FileList['test/*_test.rb']
22
- t.verbose = true
23
- end
24
-
25
- # For mongoid
26
- ENV['RACK_ENV'] = 'test'
27
-
28
- Rake::Task['functional'].invoke
19
+ Rake::TestTask.new(:test) do |t|
20
+ t.pattern = 'test/**/*_test.rb'
21
+ t.verbose = true
22
+ t.warning = false
29
23
  end
30
24
 
31
- task :default => :test
25
+ # By default run tests against all appraisals
26
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
27
+ require 'appraisal'
28
+ task default: :appraisal
29
+ else
30
+ task default: :test
31
+ end
@@ -4,9 +4,9 @@
4
4
  ---
5
5
  # For the development and test environments the test symmetric encryption keys
6
6
  # can be placed directly in the source code.
7
- # And therefore no RSA private key is required
7
+ # And therefore no key encryption key is required
8
8
  development: &development_defaults
9
- key: 1234567890ABCDEF1234567890ABCDEF
9
+ key: 1234567890ABCDEF
10
10
  iv: 1234567890ABCDEF
11
11
  cipher: aes-128-cbc
12
12
 
@@ -14,12 +14,12 @@ test:
14
14
  <<: *development_defaults
15
15
 
16
16
  production:
17
- # Since the key to encrypt and decrypt with must NOT be stored along with the
18
- # source code, we only hold a RSA key that is used to unlock the file
19
- # containing the actual symmetric encryption key
17
+ # Since the encryption key must NOT be stored along with the
18
+ # source code, only store the key encryption key here.
20
19
  #
21
- # Sample RSA Key, DO NOT use this RSA key, generate a new one using
22
- # openssl genrsa 2048
20
+ # Test Key encryption key, DO NOT use this key, generate a new one using
21
+ # SymmetricEncryption::KeyEncryptionKey.generate
22
+ # Or use the rails generator to create a new config file as described in the readme
23
23
  private_rsa_key: |
24
24
  -----BEGIN RSA PRIVATE KEY-----
25
25
  MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH
@@ -49,11 +49,11 @@ production:
49
49
  r1URaMAun2PfAB4g2N/kEZTExgeOGqXjFhvvjdzl97ux2cTyZhaTXg==
50
50
  -----END RSA PRIVATE KEY-----
51
51
 
52
- # List Symmetric Key Ciphers in the order of current / latest first
52
+ # List Symmetric Key Ciphers in the order of current / newest first
53
53
  ciphers:
54
- # Filename containing Symmetric Encryption Key encrypted using the
55
- # RSA public key derived from the private key above
56
- - key_filename: /etc/rails/.rails.key
54
+ -
55
+ # Name of the file containing the encrypted key and iv.
56
+ key_filename: /etc/rails/.rails.key
57
57
  iv_filename: /etc/rails/.rails.iv
58
58
 
59
59
  # Encryption cipher
@@ -83,31 +83,26 @@ production:
83
83
  # Default: base64
84
84
  encoding: base64strict
85
85
 
86
- # FUTURE ENHANCEMENT:
87
- #
88
- # By adding a version indicator all encrypted data will include
89
- # an additional first Byte that includes this version number to
90
- # assist with speeding up decryption when adding new encryption keys
91
- # and to support old data decryption using older keys
92
- #
93
- # By not specifying a version, or setting it to 0 will disable version
94
- # identification prior to decrypting data
95
- # During decryption these Keys will be tried in the order listed in the
96
- # configuration file starting with the first in the list
97
- # Slower since a decryption attempt is made for every key until the
98
- # correct key is located. However, all encrypted data does not require
99
- # the 1 Byte version header prefix
100
- #
101
- # Default: 0
102
- #version: 0
86
+ # Version of this key so that when a new key is supplied, old encrypted data can be decrypted
87
+ # using the correct key.
88
+ # Increment this version with every time a new key is generated.
89
+ version: 2
90
+
91
+ # Highly Recommended to always set this to true.
92
+ # Add a header to every encrypted message.
93
+ always_add_header: true
94
+
95
+ # OPTIONAL:
96
+ #
97
+ # Any previous Symmetric Encryption Keys
98
+ #
99
+ # Only used when old data still exists that requires old decryption keys
100
+ # to be used
101
+ -
102
+ key_filename: /etc/rails/.rails_old.key
103
+ iv_filename: /etc/rails/.rails_old.iv
104
+ cipher: aes-256-cbc
105
+ encoding: base64strict
106
+ version: 1
107
+ always_add_header: true
103
108
 
104
- # OPTIONAL:
105
- #
106
- # Any previous Symmetric Encryption Keys
107
- #
108
- # Only used when old data still exists that requires old decryption keys
109
- # to be used
110
- - key_filename: /etc/rails/.rails_old.key
111
- iv_filename: /etc/rails/.rails_old.iv
112
- cipher: aes-256-cbc
113
- encoding: base64strict
@@ -4,9 +4,9 @@
4
4
  ---
5
5
  # For the development and test environments the test symmetric encryption keys
6
6
  # can be placed directly in the source code.
7
- # And therefore no RSA private key is required
7
+ # And therefore no key encryption key is required
8
8
  development: &development_defaults
9
- key: 1234567890ABCDEF1234567890ABCDEF
9
+ key: 1234567890ABCDEF
10
10
  iv: 1234567890ABCDEF
11
11
  cipher_name: aes-128-cbc
12
12
  encoding: :base64strict
@@ -16,17 +16,15 @@ test:
16
16
  <<: *development_defaults
17
17
 
18
18
  release:
19
- # Since the key to encrypt and decrypt with must NOT be stored along with the
20
- # source code, we only hold a RSA key that is used to unlock the file
21
- # containing the actual symmetric encryption key
19
+ # Since the encryption key must NOT be stored along with the
20
+ # source code, only store the key encryption key here.
22
21
  private_rsa_key: |
23
- <%= OpenSSL::PKey::RSA.generate(2048).to_s.each_line.collect { |line| " #{line}" }.join('') %>
22
+ <%= SymmetricEncryption::KeyEncryptionKey.generate.each_line.collect { |line| " #{line}" }.join('') %>
24
23
 
25
24
  # List Symmetric Key files in the order of current / latest first
26
25
  ciphers:
27
26
  -
28
- # Filename containing Symmetric Encryption Key encrypted using the
29
- # RSA public key derived from the private key above
27
+ # Name of the file containing the encrypted key and iv.
30
28
  key_filename: <%= File.join(key_path, "#{app_name}_release.key") %>
31
29
  iv_filename: <%= File.join(key_path, "#{app_name}_release.iv") %>
32
30
  cipher_name: aes-256-cbc
@@ -35,17 +33,15 @@ release:
35
33
  always_add_header: true
36
34
 
37
35
  production:
38
- # Since the key to encrypt and decrypt with must NOT be stored along with the
39
- # source code, we only hold a RSA key that is used to unlock the file
40
- # containing the actual symmetric encryption key
36
+ # Since the encryption key must NOT be stored along with the
37
+ # source code, only store the key encryption key here.
41
38
  private_rsa_key: |
42
- <%= OpenSSL::PKey::RSA.generate(2048).to_s.each_line.collect { |line| " #{line}" }.join('') %>
39
+ <%= SymmetricEncryption::KeyEncryptionKey.generate.each_line.collect { |line| " #{line}" }.join('') %>
43
40
 
44
41
  # List Symmetric Key files in the order of current / latest first
45
42
  ciphers:
46
43
  -
47
- # Filename containing Symmetric Encryption Key encrypted using the
48
- # RSA public key derived from the private key above
44
+ # Name of the file containing the encrypted key and iv.
49
45
  key_filename: <%= File.join(key_path, "#{app_name}_production.key") %>
50
46
  iv_filename: <%= File.join(key_path, "#{app_name}_production.iv") %>
51
47
  cipher_name: aes-256-cbc
@@ -4,9 +4,9 @@
4
4
  ---
5
5
  # For the development and test environments the test symmetric encryption keys
6
6
  # can be placed directly in the source code.
7
- # And therefore no RSA private key is required
7
+ # And therefore no key encryption key is required
8
8
  development: &development_defaults
9
- key: 1234567890ABCDEF1234567890ABCDEF
9
+ key: 1234567890ABCDEF
10
10
  iv: 1234567890ABCDEF
11
11
  cipher_name: aes-128-cbc
12
12
  encoding: :base64strict
@@ -15,60 +15,63 @@ test:
15
15
  <<: *development_defaults
16
16
 
17
17
  <%
18
- cipher_name = 'aes-256-cbc'
19
- rsa_key = OpenSSL::PKey::RSA.generate(2048)
20
- key_pair = SymmetricEncryption::Cipher.random_key_pair(cipher_name)
21
- iv = ::Base64.strict_encode64(key_pair[:iv])
22
- encrypted_key = ::Base64.strict_encode64(rsa_key.public_encrypt(key_pair[:key]))
18
+ rsa_key = SymmetricEncryption::KeyEncryptionKey.generate
19
+ cipher_conf = SymmetricEncryption::Cipher.generate_random_keys(private_rsa_key: rsa_key, encrypted_key: '', encrypted_iv: '')
20
+ cipher_name = cipher_conf[:cipher_name]
21
+ encrypted_iv = cipher_conf[:encrypted_iv]
22
+ encrypted_key = cipher_conf[:encrypted_key]
23
23
 
24
24
  puts "\n\n********************************************************************************"
25
25
  puts "Add the release environment key to Heroku: (Optional)\n\n"
26
26
  puts " heroku config:add RELEASE_KEY1=#{encrypted_key}\n\n"
27
+ puts "\n\n********************************************************************************"
28
+ puts "Add the release environment key to Heroku: (Optional)\n\n"
29
+ puts " heroku config:add RELEASE_IV1=#{encrypted_iv}\n\n"
27
30
  -%>
28
31
  release:
29
- # Since the key to encrypt and decrypt with must NOT be stored along with the
30
- # source code, we only hold a RSA key that is used to unlock the file
31
- # containing the actual symmetric encryption key
32
+ # Key encryption key
33
+ # Key used to secure the encryption key when it is stored in a file or encrypted.
32
34
  private_rsa_key: |
33
- <%= rsa_key.to_s.each_line.collect { |line| " #{line}" }.join('') %>
35
+ <%= rsa_key.each_line.collect { |line| " #{line}" }.join('') %>
34
36
 
35
37
  # List Symmetric Key files in the order of current / latest first
36
38
  ciphers:
37
39
  -
38
40
  # Filename containing Symmetric Encryption Key encrypted using the
39
- # RSA public key derived from the private key above
41
+ # key encryption key above (private_rsa_key).
40
42
  encrypted_key: "<%= '<' + "%= ENV['RELEASE_KEY1'] %" + '>' %>"
41
- iv: "<%= iv %>"
43
+ encrypted_iv: "<%= '<' + "%= ENV['RELEASE_IV1'] %" + '>' %>"
42
44
  cipher_name: <%= cipher_name %>
43
45
  encoding: :base64strict
44
46
  version: 1
45
47
  always_add_header: true
46
48
 
47
49
  <%
48
- cipher_name = 'aes-256-cbc'
49
- rsa_key = OpenSSL::PKey::RSA.generate(2048)
50
- key_pair = SymmetricEncryption::Cipher.random_key_pair(cipher_name)
51
- iv = ::Base64.strict_encode64(key_pair[:iv])
52
- encrypted_key = ::Base64.strict_encode64(rsa_key.public_encrypt(key_pair[:key]))
50
+ rsa_key = SymmetricEncryption::KeyEncryptionKey.generate
51
+ cipher_conf = SymmetricEncryption::Cipher.generate_random_keys(private_rsa_key: rsa_key, encrypted_key: '', encrypted_iv: '')
52
+ cipher_name = cipher_conf[:cipher_name]
53
+ encrypted_iv = cipher_conf[:encrypted_iv]
54
+ encrypted_key = cipher_conf[:encrypted_key]
53
55
 
54
56
  puts "Add the production key to Heroku:\n\n"
55
57
  puts " heroku config:add PRODUCTION_KEY1=#{encrypted_key}\n\n"
56
58
  puts "********************************************************************************\n\n\n"
59
+ puts "Add the production key to Heroku:\n\n"
60
+ puts " heroku config:add PRODUCTION_IV1=#{encrypted_iv}\n\n"
61
+ puts "********************************************************************************\n\n\n"
57
62
  -%>
58
63
  production:
59
- # Since the key to encrypt and decrypt with must NOT be stored along with the
60
- # source code, we only hold a RSA key that is used to unlock the file
61
- # containing the actual symmetric encryption key
64
+ # Since the encryption key must NOT be stored along with the
65
+ # source code, only store the key encryption key here.
62
66
  private_rsa_key: |
63
- <%= rsa_key.to_s.each_line.collect { |line| " #{line}" }.join('') %>
67
+ <%= rsa_key.each_line.collect { |line| " #{line}" }.join('') %>
64
68
 
65
69
  # List Symmetric Key files in the order of current / latest first
66
70
  ciphers:
67
71
  -
68
- # Filename containing Symmetric Encryption Key encrypted using the
69
- # RSA public key derived from the private key above
72
+ # Encrypted key is supplied via an environment variable.
70
73
  encrypted_key: "<%= '<' + "%= ENV['PRODUCTION_KEY1'] %" + '>' %>"
71
- iv: "<%= iv %>"
74
+ encrypted_iv: "<%= '<' + "%= ENV['PRODUCTION_IV1'] %" + '>' %>"
72
75
  cipher_name: <%= cipher_name %>
73
76
  encoding: :base64strict
74
77
  version: 1
@@ -10,11 +10,16 @@ require 'symmetric_encryption/exception'
10
10
 
11
11
  #@formatter:off
12
12
  module SymmetricEncryption
13
- autoload :Coerce, 'symmetric_encryption/coerce'
14
- autoload :Config, 'symmetric_encryption/config'
15
- autoload :Reader, 'symmetric_encryption/reader'
16
- autoload :Writer, 'symmetric_encryption/writer'
17
- autoload :Generator, 'symmetric_encryption/generator'
13
+ autoload :Coerce, 'symmetric_encryption/coerce'
14
+ autoload :Config, 'symmetric_encryption/config'
15
+ autoload :Encoder, 'symmetric_encryption/encoder'
16
+ autoload :KeyEncryptionKey, 'symmetric_encryption/key_encryption_key'
17
+ autoload :Reader, 'symmetric_encryption/reader'
18
+ autoload :Writer, 'symmetric_encryption/writer'
19
+ autoload :Generator, 'symmetric_encryption/generator'
20
+ module Utils
21
+ autoload :ReEncryptConfigFiles, 'symmetric_encryption/re_encrypt_config_files'
22
+ end
18
23
  end
19
24
  #@formatter:on
20
25
 
@@ -25,4 +30,7 @@ if defined?(ActiveRecord::Base) && !defined?(AttrEncrypted::Version)
25
30
  end
26
31
  require 'symmetric_encryption/railties/symmetric_encryption_validator' if defined?(ActiveModel)
27
32
  require 'symmetric_encryption/extensions/mongoid/encrypted' if defined?(Mongoid)
28
- require 'symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key' if defined?(MongoMapper)
33
+ if defined?(MongoMapper)
34
+ warn 'MongoMapper support is deprecated. Consider upgrading to Mongoid.'
35
+ require 'symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key'
36
+ end
@@ -1,5 +1,5 @@
1
+ require 'openssl'
1
2
  module SymmetricEncryption
2
-
3
3
  # Hold all information related to encryption keys
4
4
  # as well as encrypt and decrypt data using those keys
5
5
  #
@@ -7,11 +7,9 @@ module SymmetricEncryption
7
7
  # threads at the same time without needing an instance of Cipher per thread
8
8
  class Cipher
9
9
  # Cipher to use for encryption and decryption
10
- attr_reader :cipher_name, :version, :iv
11
- attr_accessor :encoding, :always_add_header
12
-
13
- # Available encodings
14
- ENCODINGS = [:none, :base64, :base64strict, :base16]
10
+ attr_accessor :cipher_name, :version, :iv, :always_add_header
11
+ attr_reader :encoder, :encoding
12
+ attr_writer :key
15
13
 
16
14
  # Backward compatibility
17
15
  alias_method :cipher, :cipher_name
@@ -52,48 +50,104 @@ module SymmetricEncryption
52
50
  }
53
51
  end
54
52
 
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 = params[: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 = params.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
53
+ # Generate new randomized keys and generate key and iv files if supplied.
54
+ # Overwrites key files for the current environment.
55
+ #
56
+ # Parameters
57
+ # :key_filename
58
+ # Name of file that will contain the symmetric key encrypted using the public
59
+ # key from the private_rsa_key.
60
+ # Or,
61
+ # :encrypted_key
62
+ # Symmetric key encrypted using the public key from the private_rsa_key
63
+ # and then Base64 encoded
64
+ #
65
+ # Note:
66
+ # If :key_filename and :encrypted_key are not supplied then a new :key will be returned.
67
+ # :key is the Symmetric Key to use for encryption and decryption.
68
+ #
69
+ #
70
+ # :iv_filename
71
+ # Name of file containing symmetric key initialization vector
72
+ # encrypted using the public key from the private_rsa_key
73
+ # Deprecated: It is _not_ necessary to encrypt the initialization vector (IV)
74
+ # Or,
75
+ # :encrypted_iv
76
+ # Initialization vector encrypted using the public key from the private_rsa_key
77
+ # and then Base64 encoded
78
+ # Deprecated: It is _not_ necessary to encrypt the initialization vector (IV)
79
+ #
80
+ # Note:
81
+ # If :iv_filename and :encrypted_iv are not supplied then a new :iv will be returned.
82
+ # :key is the Initialization Vector to use with Symmetric Key.
83
+ #
84
+ #
85
+ # private_rsa_key [String]
86
+ # Key encryption key.
87
+ # To generate a new one: SymmetricEncryption::KeyEncryptionKey.generate
88
+ # Required if :key_filename, :encrypted_key, :iv_filename, or :encrypted_iv is supplied
89
+ #
90
+ # :cipher_name [String]
91
+ # Encryption Cipher to use.
92
+ # Default: aes-256-cbc
93
+ #
94
+ # :encoding [Symbol]
95
+ # :base64strict
96
+ # Return as a base64 encoded string that does not include additional newlines
97
+ # This is the recommended format since newlines in the values to
98
+ # SQL queries are cumbersome. Also the newline reformatting is unnecessary
99
+ # It is not the default for backward compatibility
100
+ # :base64
101
+ # Return as a base64 encoded string
102
+ # :base16
103
+ # Return as a Hex encoded string
104
+ # :none
105
+ # Return as raw binary data string. Note: String can contain embedded nulls
106
+ # Default: :base64strict
107
+ def self.generate_random_keys(params = {})
108
+ params = params.dup
109
+ private_rsa_key = params.delete(:private_rsa_key)
110
+ cipher_name = params.delete(:cipher_name) || 'aes-256-cbc'
111
+ encoding = params.delete(:encoding) || :base64strict
112
+ unless private_rsa_key
113
+ [:key_filename, :encrypted_key, :iv_filename, :encrypted_iv].each do |key|
114
+ raise(SymmetricEncryption::ConfigError, "When :#{key} is supplied, :private_rsa_key is required.") if params.include?(key)
115
+ end
116
+ end
117
+
118
+ key_encryption_key = KeyEncryptionKey.new(private_rsa_key) if private_rsa_key
119
+ cipher_conf = {cipher_name: cipher_name, encoding: encoding}
120
+
121
+ key_pair = SymmetricEncryption::Cipher.random_key_pair(cipher_name)
122
+ key = key_pair[:key]
123
+ iv = key_pair[:iv]
124
+
125
+ if file_name = params.delete(:key_filename)
126
+ cipher_conf[:key_filename] = file_name
127
+ encrypted_key = key_encryption_key.encrypt(key)
128
+ write_to_file(file_name, encrypted_key)
129
+ elsif params.delete(:encrypted_key)
130
+ encrypted_key = key_encryption_key.encrypt(key)
131
+ cipher_conf[:encrypted_key] = SymmetricEncryption::Encoder[encoding].encode(encrypted_key)
132
+ else
133
+ params.delete(:key)
134
+ cipher_conf[:key] = SymmetricEncryption::Encoder[encoding].encode(key.to_s)
80
135
  end
81
136
 
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 = params.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
137
+ if file_name = params.delete(:iv_filename)
138
+ cipher_conf[:iv_filename] = file_name
139
+ encrypted_iv = key_encryption_key.encrypt(iv)
140
+ write_to_file(file_name, encrypted_iv)
141
+ elsif params.delete(:encrypted_iv)
142
+ encrypted_iv = key_encryption_key.encrypt(iv)
143
+ cipher_conf[:encrypted_iv] = SymmetricEncryption::Encoder[encoding].encode(encrypted_iv)
144
+ else
145
+ params.delete(:iv)
146
+ cipher_conf[:iv] = SymmetricEncryption::Encoder[encoding].encode(iv.to_s)
96
147
  end
148
+
149
+ raise(ArgumentError, "SymmetricEncryption::Cipher Invalid options #{params.inspect}") if params.size > 0
150
+ cipher_conf
97
151
  end
98
152
 
99
153
  # Create a Symmetric::Key for encryption and decryption purposes
@@ -114,15 +168,15 @@ module SymmetricEncryption
114
168
  # Optional. The Initialization Vector to use with Symmetric Key
115
169
  # Highly Recommended as it is the input into the CBC algorithm
116
170
  # Or,
117
- # Note: The following 2 options are deprecated since it is _not_ necessary
118
- # to encrypt the initialization vector (IV)
119
171
  # :iv_filename
120
172
  # Name of file containing symmetric key initialization vector
121
173
  # encrypted using the public key from the private_rsa_key
174
+ # Deprecated: It is _not_ necessary to encrypt the initialization vector (IV)
122
175
  # Or,
123
176
  # :encrypted_iv
124
177
  # Initialization vector encrypted using the public key from the private_rsa_key
125
178
  # and then Base64 encoded
179
+ # Deprecated: It is _not_ necessary to encrypt the initialization vector (IV)
126
180
  #
127
181
  # :cipher_name [String]
128
182
  # Optional. Encryption Cipher to use
@@ -157,41 +211,57 @@ module SymmetricEncryption
157
211
  # Recommended: true
158
212
  #
159
213
  # 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
214
+ # Key encryption key.
215
+ # To generate a new one: SymmetricEncryption::KeyEncryptionKey.generate
216
+ # Required if :key_filename, :encrypted_key, :iv_filename, or :encrypted_iv is supplied
162
217
  def initialize(params={})
163
218
  params = params.dup
164
219
  @cipher_name = params.delete(:cipher_name) || params.delete(:cipher) || 'aes-256-cbc'
165
220
  @version = params.delete(:version)
166
221
  @always_add_header = params.delete(:always_add_header) || false
167
- @encoding = (params.delete(:encoding) || :base64).to_sym
168
-
169
- # To decrypt encrypted key or iv files
222
+ self.encoding = (params.delete(:encoding) || :base64).to_sym
170
223
  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)
224
+ unless private_rsa_key
225
+ [:key_filename, :encrypted_key, :iv_filename, :encrypted_iv].each do |key|
226
+ raise(SymmetricEncryption::ConfigError, "When :#{key} is supplied, :private_rsa_key is required.") if params.include?(key)
227
+ end
179
228
  end
180
229
 
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
230
+ key_encryption_key = KeyEncryptionKey.new(private_rsa_key) if private_rsa_key
231
+ @key =
232
+ if key = params.delete(:key)
233
+ key
234
+ elsif file_name = params.delete(:key_filename)
235
+ encrypted_key = self.class.read_from_file(file_name)
236
+ key_encryption_key.decrypt(encrypted_key)
237
+ elsif encrypted_key = params.delete(:encrypted_key)
238
+ binary = self.encoder.decode(encrypted_key)
239
+ key_encryption_key.decrypt(binary)
240
+ else
241
+ raise(ArgumentError, 'Missing mandatory parameter :key, :key_filename, or :encrypted_key')
242
+ end
243
+
244
+ @iv =
245
+ if iv = params.delete(:iv)
246
+ iv
247
+ elsif file_name = params.delete(:iv_filename)
248
+ encrypted_iv = self.class.read_from_file(file_name)
249
+ key_encryption_key.decrypt(encrypted_iv)
250
+ elsif encrypted_iv = params.delete(:encrypted_iv)
251
+ binary = self.encoder.decode(encrypted_iv)
252
+ key_encryption_key.decrypt(binary)
253
+ end
188
254
 
189
- raise(ArgumentError, 'Missing mandatory parameter :key, :key_filename, or :encrypted_key') unless @key
190
- raise(ArgumentError, "Invalid Encoding: #{@encoding}") unless ENCODINGS.include?(@encoding)
191
255
  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)
192
256
  raise(ArgumentError, "SymmetricEncryption::Cipher Invalid options #{params.inspect}") if params.size > 0
193
257
  end
194
258
 
259
+ # Change the encoding
260
+ def encoding=(encoding)
261
+ @encoder = SymmetricEncryption::Encoder[encoding]
262
+ @encoding = encoding
263
+ end
264
+
195
265
  # Encrypt and then encode a string
196
266
  #
197
267
  # Returns data encrypted and then encoded according to the encoding setting
@@ -277,21 +347,7 @@ module SymmetricEncryption
277
347
  # Returned string is UTF8 encoded except for encoding :none
278
348
  def encode(binary_string)
279
349
  return binary_string if binary_string.nil? || (binary_string == '')
280
-
281
- # Now encode data based on encoding setting
282
- case encoding
283
- when :base64
284
- encoded_string = ::Base64.encode64(binary_string)
285
- encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
286
- when :base64strict
287
- encoded_string = ::Base64.encode64(binary_string).gsub(/\n/, '')
288
- encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
289
- when :base16
290
- encoded_string = binary_string.to_s.unpack('H*').first
291
- encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
292
- else
293
- binary_string
294
- end
350
+ encoder.encode(binary_string)
295
351
  end
296
352
 
297
353
  # Decode the supplied string using the encoding in this cipher instance
@@ -300,17 +356,7 @@ module SymmetricEncryption
300
356
  # Returned string is Binary encoded
301
357
  def decode(encoded_string)
302
358
  return encoded_string if encoded_string.nil? || (encoded_string == '')
303
-
304
- case encoding
305
- when :base64, :base64strict
306
- decoded_string = ::Base64.decode64(encoded_string)
307
- decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING)
308
- when :base16
309
- decoded_string = [encoded_string].pack('H*')
310
- decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING)
311
- else
312
- encoded_string
313
- end
359
+ encoder.decode(encoded_string)
314
360
  end
315
361
 
316
362
  # Return a new random key using the configured cipher_name
@@ -548,44 +594,19 @@ module SymmetricEncryption
548
594
 
549
595
  attr_reader :key
550
596
 
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: bin/rails generate symmetric_encryption:new_keys production\n\n"
560
- end
597
+ # Read from the file, raising an exception if it is not found
598
+ def self.read_from_file(file_name)
599
+ File.open(file_name, 'rb') { |f| f.read }
600
+ rescue Errno::ENOENT => exc
601
+ puts "\nSymmetric Encryption key file: '#{file_name}' not found or readable."
602
+ puts "To generate the keys for the first time run: bin/rails generate symmetric_encryption:new_keys production\n\n"
603
+ raise(exc)
561
604
  end
562
605
 
563
- # Save symmetric key after encrypting it with the private RSA key
564
- # Backing up existing files if present
565
- def self.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
606
+ # Write to the supplied filename, backing up the existing file if present
607
+ def self.write_to_file(file_name, data)
567
608
  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))
609
+ File.open(file_name, 'wb') { |file| file.write(data) }
589
610
  end
590
611
 
591
612
  end