symmetric-encryption 3.9.1 → 4.0.0.beta3
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 +72 -0
- data/bin/symmetric-encryption +5 -0
- data/lib/symmetric_encryption/cipher.rb +162 -419
- data/lib/symmetric_encryption/cli.rb +343 -0
- data/lib/symmetric_encryption/coerce.rb +5 -20
- data/lib/symmetric_encryption/config.rb +128 -50
- data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +2 -2
- data/lib/symmetric_encryption/generator.rb +3 -2
- data/lib/symmetric_encryption/header.rb +260 -0
- data/lib/symmetric_encryption/key.rb +106 -0
- data/lib/symmetric_encryption/keystore/environment.rb +90 -0
- data/lib/symmetric_encryption/keystore/file.rb +102 -0
- data/lib/symmetric_encryption/keystore/memory.rb +53 -0
- data/lib/symmetric_encryption/keystore.rb +124 -0
- data/lib/symmetric_encryption/railtie.rb +5 -7
- data/lib/symmetric_encryption/reader.rb +74 -55
- data/lib/symmetric_encryption/rsa_key.rb +24 -0
- data/lib/symmetric_encryption/symmetric_encryption.rb +64 -102
- data/lib/symmetric_encryption/utils/re_encrypt_files.rb +140 -0
- data/lib/symmetric_encryption/version.rb +1 -1
- data/lib/symmetric_encryption/writer.rb +104 -117
- data/lib/symmetric_encryption.rb +9 -4
- data/test/active_record_test.rb +61 -40
- data/test/cipher_test.rb +179 -236
- data/test/config/symmetric-encryption.yml +140 -82
- data/test/header_test.rb +218 -0
- data/test/key_test.rb +231 -0
- data/test/keystore/environment_test.rb +119 -0
- data/test/keystore/file_test.rb +125 -0
- data/test/keystore_test.rb +59 -0
- data/test/mongoid_test.rb +13 -13
- data/test/reader_test.rb +52 -53
- data/test/symmetric_encryption_test.rb +50 -135
- data/test/test_db.sqlite3 +0 -0
- data/test/writer_test.rb +52 -31
- metadata +26 -14
- data/examples/symmetric-encryption.yml +0 -108
- data/lib/rails/generators/symmetric_encryption/config/config_generator.rb +0 -22
- data/lib/rails/generators/symmetric_encryption/config/templates/symmetric-encryption.yml +0 -50
- data/lib/rails/generators/symmetric_encryption/heroku_config/heroku_config_generator.rb +0 -20
- data/lib/rails/generators/symmetric_encryption/heroku_config/templates/symmetric-encryption.yml +0 -78
- data/lib/rails/generators/symmetric_encryption/new_keys/new_keys_generator.rb +0 -14
- data/lib/symmetric_encryption/key_encryption_key.rb +0 -32
- data/lib/symmetric_encryption/railties/symmetric_encryption.rake +0 -84
- data/lib/symmetric_encryption/utils/re_encrypt_config_files.rb +0 -82
@@ -0,0 +1,140 @@
|
|
1
|
+
# Used for re-encrypting encrypted passwords stored in configuration files.
|
2
|
+
#
|
3
|
+
# Search for any encrypted value and re-encrypt it using the latest encryption key.
|
4
|
+
# Note:
|
5
|
+
# * Only works with encrypted values that have the standard header.
|
6
|
+
# * The search looks for the header and then replaces the encrypted value.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
# re_encrypt = SymmetricEncryption::Utils::ReEncryptConfigFiles.new(version: 4)
|
10
|
+
# re_encrypt.process_directory('../../**/*.yml')
|
11
|
+
#
|
12
|
+
# Notes:
|
13
|
+
# * Only supports the output from encrypting data.
|
14
|
+
# * I.e. Manually adding newlines to base 64 output is not supported.
|
15
|
+
# * For now only supports one encrypted value per line.
|
16
|
+
module SymmetricEncryption
|
17
|
+
module Utils
|
18
|
+
# ReEncrypt files
|
19
|
+
#
|
20
|
+
# If a file is encrypted, it is re-encrypted with the cipher that has the highest version number.
|
21
|
+
# A file that is already encrypted with the specified key version is not re-encrypted.
|
22
|
+
# If an encrypted value cannot be decypted in the current environment it is left unmodified.
|
23
|
+
#
|
24
|
+
# If a file is not encrypted, the file is searched for any encrypted values, and those values are re-encrypted.
|
25
|
+
#
|
26
|
+
# symmetric_encryption --reencrypt "**/*.yml"
|
27
|
+
class ReEncryptFiles
|
28
|
+
attr_accessor :cipher, :version
|
29
|
+
|
30
|
+
# Parameters:
|
31
|
+
# version: [Integer]
|
32
|
+
# Version of the encryption key to use when re-encrypting the value.
|
33
|
+
# Default: Default cipher ( first in the list of configured ciphers )
|
34
|
+
def initialize(version: SymmetricEncryption.cipher.version)
|
35
|
+
@version = version || SymmetricEncryption.cipher.version
|
36
|
+
@cipher = SymmetricEncryption.cipher(@version)
|
37
|
+
raise(ArgumentError, "Undefined encryption key version: #{version}") if @cipher.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
# Re-encrypt the supplied encrypted value with the new cipher
|
41
|
+
def re_encrypt(encrypted)
|
42
|
+
if unencrypted = SymmetricEncryption.try_decrypt(encrypted)
|
43
|
+
cipher.encrypt(unencrypted)
|
44
|
+
else
|
45
|
+
encrypted
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Process a single file.
|
50
|
+
#
|
51
|
+
# Returns [Integer] number of encrypted values re-encrypted.
|
52
|
+
def re_encrypt_contents(file_name)
|
53
|
+
return 0 if File.size(file_name) > 256 * 1024
|
54
|
+
|
55
|
+
hits = 0
|
56
|
+
lines = File.read(file_name)
|
57
|
+
output_lines = ''
|
58
|
+
r = regexp
|
59
|
+
lines.each_line do |line|
|
60
|
+
line.force_encoding(SymmetricEncryption::UTF8_ENCODING)
|
61
|
+
output_lines <<
|
62
|
+
if line.valid_encoding? && (result = line.match(r))
|
63
|
+
encrypted = result[0]
|
64
|
+
new_value = re_encrypt(encrypted)
|
65
|
+
if new_value != encrypted
|
66
|
+
hits += 1
|
67
|
+
line.gsub(encrypted, new_value)
|
68
|
+
else
|
69
|
+
line
|
70
|
+
end
|
71
|
+
else
|
72
|
+
line
|
73
|
+
end
|
74
|
+
end
|
75
|
+
if hits
|
76
|
+
File.open(file_name, 'wb') { |file| file.write(output_lines) }
|
77
|
+
end
|
78
|
+
hits
|
79
|
+
rescue
|
80
|
+
puts "Failed re-encrypting the file contents of: #{file_name}"
|
81
|
+
raise
|
82
|
+
end
|
83
|
+
|
84
|
+
# Re Encrypt an entire file
|
85
|
+
def re_encrypt_file(file_name)
|
86
|
+
temp_file_name = "__re_encrypting_#{file_name}"
|
87
|
+
SymmetricEncryption::Reader.open(file_name) do |source|
|
88
|
+
SymmetricEncryption::Writer.encrypt(source: source, target: temp_file_name, compress: true, version: version)
|
89
|
+
end
|
90
|
+
File.delete(file_name)
|
91
|
+
File.rename(temp_file_name, file_name)
|
92
|
+
rescue
|
93
|
+
File.delete(temp_file_name) if temp_file_name && File.exist?(temp_file_name)
|
94
|
+
raise
|
95
|
+
end
|
96
|
+
|
97
|
+
# Process a directory of files.
|
98
|
+
#
|
99
|
+
# Parameters:
|
100
|
+
# path: [String]
|
101
|
+
# Search path to look for files in.
|
102
|
+
# Example: '../../**/*.yml'
|
103
|
+
def process_directory(path)
|
104
|
+
Dir[path].each do |file_name|
|
105
|
+
next if File.directory?(file_name)
|
106
|
+
|
107
|
+
if v = encrypted_file_version(file_name)
|
108
|
+
if v == version
|
109
|
+
puts "Skipping already re-encrypted file: #{file_name}"
|
110
|
+
else
|
111
|
+
puts "Re-encrypting entire file: #{file_name}"
|
112
|
+
re_encrypt_file(file_name)
|
113
|
+
end
|
114
|
+
else
|
115
|
+
count = re_encrypt_contents(file_name)
|
116
|
+
puts "Re-encrypted #{count} encrypted value(s) in: #{file_name}" if count > 0
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def regexp
|
124
|
+
@regexp ||= /#{SymmetricEncryption.cipher.encoded_magic_header}([A-Za-z0-9+\/]+=+[\\n]*)/
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns [Integer] encrypted file key version.
|
128
|
+
# Returns [nil] if the file is not encrypted or does not have a header.
|
129
|
+
def encrypted_file_version(file_name)
|
130
|
+
::File.open(file_name, 'rb') do |file|
|
131
|
+
reader = SymmetricEncryption::Reader.new(file)
|
132
|
+
reader.version if reader.header_present?
|
133
|
+
end
|
134
|
+
rescue OpenSSL::Cipher::CipherError
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -1,89 +1,38 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
|
3
3
|
module SymmetricEncryption
|
4
|
-
# Write to encrypted files and other IO streams
|
4
|
+
# Write to encrypted files and other IO streams.
|
5
5
|
#
|
6
6
|
# Features:
|
7
7
|
# * Encryption on the fly whilst writing files.
|
8
|
-
# * Large file support by only buffering small amounts of data in memory
|
8
|
+
# * Large file support by only buffering small amounts of data in memory.
|
9
9
|
# * Underlying buffering to ensure that encrypted data fits
|
10
|
-
# into the Symmetric Encryption Cipher block size
|
11
|
-
# Only the last block in the file will be padded if it is less than the block size
|
10
|
+
# into the Symmetric Encryption Cipher block size.
|
11
|
+
# Only the last block in the file will be padded if it is less than the block size.
|
12
12
|
class Writer
|
13
|
-
# Open a file for writing, or use the supplied IO Stream
|
13
|
+
# Open a file for writing, or use the supplied IO Stream.
|
14
14
|
#
|
15
15
|
# Parameters:
|
16
|
-
#
|
17
|
-
# The
|
16
|
+
# file_name_or_stream: [String|IO]
|
17
|
+
# The file_name to open if a string, otherwise the stream to use.
|
18
18
|
# The file or stream will be closed on completion, use .initialize to
|
19
|
-
# avoid having the stream closed automatically
|
20
|
-
#
|
21
|
-
# options:
|
22
|
-
# :compress [true|false]
|
23
|
-
# Uses Zlib to compress the data before it is encrypted and
|
24
|
-
# written to the file
|
25
|
-
# If true, it forces header to true.
|
26
|
-
# Default: false
|
27
|
-
#
|
28
|
-
# :random_key [true|false]
|
29
|
-
# Generates a new random key for every new file or stream
|
30
|
-
# If true, it forces header to true. Version below then has no effect
|
31
|
-
# The Random key will be written to the file/stream in encrypted
|
32
|
-
# form as part of the header
|
33
|
-
# The key is encrypted using the global key
|
34
|
-
# Default: true
|
35
|
-
# Recommended: true.
|
36
|
-
# Setting to false will eventually expose the
|
37
|
-
# encryption key since too much data will be encrypted using the
|
38
|
-
# same encryption key
|
39
|
-
#
|
40
|
-
# :random_iv [true|false]
|
41
|
-
# Generates a new random iv for every new file or stream
|
42
|
-
# If true, it forces header to true.
|
43
|
-
# The Random iv will be written to the file/stream in encrypted
|
44
|
-
# form as part of the header
|
45
|
-
# Default: Value supplied above for :random_key
|
46
|
-
# Recommended: true. Setting to false will eventually expose the
|
47
|
-
# encryption key since too much data will be encrypted using the
|
48
|
-
# same encryption key
|
49
|
-
#
|
50
|
-
# :header [true|false]
|
51
|
-
# Whether to include the magic header that indicates the file
|
52
|
-
# is encrypted and whether its contents are compressed
|
53
|
-
#
|
54
|
-
# The header contains:
|
55
|
-
# Version of the encryption key used to encrypt the file
|
56
|
-
# Indicator if the data was compressed
|
57
|
-
# Default: true
|
58
|
-
#
|
59
|
-
# :version
|
60
|
-
# When random_key is true, the version of the encryption key to use
|
61
|
-
# when encrypting the header portion of the file
|
62
|
-
#
|
63
|
-
# When random_key is false, the version of the encryption key to use
|
64
|
-
# to encrypt the entire file
|
65
|
-
# Default: SymmetricEncryption.cipher
|
66
|
-
#
|
67
|
-
# :mode
|
68
|
-
# See File.open for open modes
|
69
|
-
# Default: 'w'
|
70
|
-
#
|
71
|
-
# :cipher_name
|
72
|
-
# The name of the cipher to use only if both :random_key and
|
73
|
-
# :random_iv are true.
|
74
|
-
# Default: SymmetricEncryption.cipher.cipher_name
|
19
|
+
# avoid having the stream closed automatically.
|
75
20
|
#
|
76
|
-
#
|
21
|
+
# compress: [true|false]
|
22
|
+
# Uses Zlib to compress the data before it is encrypted and
|
23
|
+
# written to the file/stream.
|
24
|
+
# Default: false
|
77
25
|
#
|
26
|
+
# Note: Compression occurs before encryption
|
78
27
|
#
|
79
28
|
# # Example: Encrypt and write data to a file
|
80
|
-
# SymmetricEncryption::Writer.open('test_file') do |file|
|
29
|
+
# SymmetricEncryption::Writer.open('test_file.enc') do |file|
|
81
30
|
# file.write "Hello World\n"
|
82
31
|
# file.write 'Keep this secret'
|
83
32
|
# end
|
84
33
|
#
|
85
34
|
# # Example: Compress, Encrypt and write data to a file
|
86
|
-
# SymmetricEncryption::Writer.open('encrypted_compressed.
|
35
|
+
# SymmetricEncryption::Writer.open('encrypted_compressed.enc', compress: true) do |file|
|
87
36
|
# file.write "Hello World\n"
|
88
37
|
# file.write "Compress this\n"
|
89
38
|
# file.write "Keep this safe and secure\n"
|
@@ -93,80 +42,115 @@ module SymmetricEncryption
|
|
93
42
|
# require 'csv'
|
94
43
|
# begin
|
95
44
|
# # Must supply :row_sep for CSV otherwise it will attempt to read from and then rewind the file
|
96
|
-
# csv = CSV.new(SymmetricEncryption::Writer.open('
|
45
|
+
# csv = CSV.new(SymmetricEncryption::Writer.open('csv.enc'), row_sep: "\n")
|
97
46
|
# csv << [1,2,3,4,5]
|
98
47
|
# ensure
|
99
48
|
# csv.close if csv
|
100
49
|
# end
|
101
|
-
def self.open(
|
102
|
-
|
103
|
-
mode = options.fetch(:mode, 'wb')
|
104
|
-
compress = options.fetch(:compress, false)
|
105
|
-
ios = filename_or_stream.is_a?(String) ? ::File.open(filename_or_stream, mode) : filename_or_stream
|
50
|
+
def self.open(file_name_or_stream, compress: false, **args)
|
51
|
+
ios = file_name_or_stream.is_a?(String) ? ::File.open(file_name_or_stream, 'wb') : file_name_or_stream
|
106
52
|
|
107
53
|
begin
|
108
|
-
file = self.new(ios,
|
54
|
+
file = self.new(ios, compress: compress, **args)
|
109
55
|
file = Zlib::GzipWriter.new(file) if compress
|
110
|
-
|
56
|
+
block_given? ? yield(file) : file
|
111
57
|
ensure
|
112
|
-
file.close if
|
58
|
+
file.close if block_given? && file && (file.respond_to?(:closed?) && !file.closed?)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Write the contents of a string in memory to an encrypted file / stream.
|
63
|
+
#
|
64
|
+
# Notes:
|
65
|
+
# * Do not use this method for writing large files.
|
66
|
+
def self.write(file_name_or_stream, data, **args)
|
67
|
+
open(file_name_or_stream, **args) { |f| f.write(data) }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Encrypt an entire file.
|
71
|
+
#
|
72
|
+
# Returns [Integer] the number of encrypted bytes written to the target file.
|
73
|
+
#
|
74
|
+
# Params:
|
75
|
+
# source: [String|IO]
|
76
|
+
# Source file_name or IOStream
|
77
|
+
#
|
78
|
+
# target: [String|IO]
|
79
|
+
# Target file_name or IOStream
|
80
|
+
#
|
81
|
+
# compress: [true|false]
|
82
|
+
# Whether to compress the target file prior to encryption.
|
83
|
+
# Default: false
|
84
|
+
#
|
85
|
+
# block_size: [Integer]
|
86
|
+
# Number of bytes to read into memory for each read.
|
87
|
+
# For very large files using a larger block size is faster.
|
88
|
+
# Default: 65535
|
89
|
+
#
|
90
|
+
# Notes:
|
91
|
+
# * The file contents are streamed so that the entire file is _not_ loaded into memory.
|
92
|
+
def self.encrypt(source:, target:, block_size: 65535, **args)
|
93
|
+
source_ios = source.is_a?(String) ? ::File.open(source, 'rb') : source
|
94
|
+
bytes_written = 0
|
95
|
+
open(target, **args) do |output_file|
|
96
|
+
while !source_ios.eof?
|
97
|
+
bytes_written += output_file.write(source_ios.read(block_size))
|
98
|
+
end
|
113
99
|
end
|
100
|
+
bytes_written
|
101
|
+
ensure
|
102
|
+
source_ios.close if source_ios && source_ios.respond_to?(:closed?) && !source_ios.closed?
|
114
103
|
end
|
115
104
|
|
116
105
|
# Encrypt data before writing to the supplied stream
|
117
|
-
def initialize(ios,
|
118
|
-
@ios = ios
|
119
|
-
header = options.fetch(:header, true)
|
120
|
-
random_key = options.fetch(:random_key, true)
|
121
|
-
random_iv = options.fetch(:random_iv, random_key)
|
122
|
-
raise(ArgumentError, 'When :random_key is true, :random_iv must also be true') if random_key && !random_iv
|
106
|
+
def initialize(ios, version: nil, cipher_name: nil, header: true, random_key: true, random_iv: true, compress: false)
|
123
107
|
# Compress is only used at this point for setting the flag in the header
|
124
|
-
|
125
|
-
|
126
|
-
cipher_name = options[:cipher_name]
|
108
|
+
@ios = ios
|
109
|
+
raise(ArgumentError, 'When :random_key is true, :random_iv must also be true') if random_key && !random_iv
|
127
110
|
raise(ArgumentError, 'Cannot supply a :cipher_name unless both :random_key and :random_iv are true') if cipher_name && !random_key && !random_iv
|
128
111
|
|
129
|
-
# Force header if compressed or using random iv, key
|
130
|
-
header = true if compress || random_key || random_iv
|
131
|
-
|
132
112
|
# Cipher to encrypt the random_key, or the entire file
|
133
113
|
cipher = SymmetricEncryption.cipher(version)
|
134
114
|
raise(SymmetricEncryption::CipherError, "Cipher with version:#{version} not found in any of the configured SymmetricEncryption ciphers") unless cipher
|
135
115
|
|
116
|
+
# Force header if compressed or using random iv, key
|
117
|
+
if (header == true) || compress || random_key || random_iv
|
118
|
+
header = Header.new(version: cipher.version, compress: compress, cipher_name: cipher_name)
|
119
|
+
end
|
120
|
+
|
136
121
|
@stream_cipher = ::OpenSSL::Cipher.new(cipher_name || cipher.cipher_name)
|
137
122
|
@stream_cipher.encrypt
|
138
123
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
124
|
+
if random_key
|
125
|
+
header.key = @stream_cipher.key = @stream_cipher.random_key
|
126
|
+
else
|
127
|
+
@stream_cipher.key = cipher.send(:key)
|
128
|
+
end
|
144
129
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
compress,
|
150
|
-
random_iv ? iv : nil,
|
151
|
-
random_key ? key : nil,
|
152
|
-
cipher_name))
|
130
|
+
if random_iv
|
131
|
+
header.iv = @stream_cipher.iv = @stream_cipher.random_iv
|
132
|
+
else
|
133
|
+
@stream_cipher.iv = cipher.iv if cipher.iv
|
153
134
|
end
|
135
|
+
|
136
|
+
@ios.write(header.to_s) if header
|
137
|
+
|
154
138
|
@size = 0
|
155
139
|
@closed = false
|
156
140
|
end
|
157
141
|
|
158
|
-
# Close the IO Stream
|
159
|
-
# Flushes any unwritten data
|
142
|
+
# Close the IO Stream.
|
160
143
|
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
144
|
+
# Notes:
|
145
|
+
# * Flushes any unwritten data.
|
146
|
+
# * Once an EncryptionWriter has been closed a new instance must be
|
147
|
+
# created before writing again.
|
148
|
+
# * Closes the passed in io stream or file.
|
149
|
+
# * `close` must be called _before_ the supplied stream is closed.
|
166
150
|
#
|
167
151
|
# It is recommended to call Symmetric::EncryptedStream.open
|
168
|
-
# rather than creating an instance of Symmetric::
|
169
|
-
# ensure that the encrypted stream is closed before the stream itself is closed
|
152
|
+
# rather than creating an instance of Symmetric::Writer directly to
|
153
|
+
# ensure that the encrypted stream is closed before the stream itself is closed.
|
170
154
|
def close(close_child_stream = true)
|
171
155
|
return if closed?
|
172
156
|
if size > 0
|
@@ -177,8 +161,9 @@ module SymmetricEncryption
|
|
177
161
|
@closed = true
|
178
162
|
end
|
179
163
|
|
180
|
-
# Write to the IO Stream as encrypted data
|
181
|
-
#
|
164
|
+
# Write to the IO Stream as encrypted data.
|
165
|
+
#
|
166
|
+
# Returns [Integer] the number of bytes written.
|
182
167
|
def write(data)
|
183
168
|
return unless data
|
184
169
|
|
@@ -189,8 +174,9 @@ module SymmetricEncryption
|
|
189
174
|
data.length
|
190
175
|
end
|
191
176
|
|
192
|
-
# Write to the IO Stream as encrypted data
|
193
|
-
#
|
177
|
+
# Write to the IO Stream as encrypted data.
|
178
|
+
#
|
179
|
+
# Returns [SymmetricEncryption::Writer] self
|
194
180
|
#
|
195
181
|
# Example:
|
196
182
|
# file << "Hello.\n" << 'This is Jack'
|
@@ -199,20 +185,21 @@ module SymmetricEncryption
|
|
199
185
|
self
|
200
186
|
end
|
201
187
|
|
202
|
-
# Flush the output stream
|
188
|
+
# Flush the output stream.
|
203
189
|
# Does not flush internal buffers since encryption requires all data to
|
204
|
-
# be written following the encryption block size
|
205
|
-
# Needed by XLS gem
|
190
|
+
# be written following the encryption block size.
|
191
|
+
# Needed by XLS gem.
|
206
192
|
def flush
|
207
193
|
@ios.flush
|
208
194
|
end
|
209
195
|
|
196
|
+
# Returns [true|false] whether this stream is closed.
|
210
197
|
def closed?
|
211
198
|
@closed || @ios.respond_to?(:closed?) && @ios.closed?
|
212
199
|
end
|
213
200
|
|
214
201
|
# Returns [Integer] the number of unencrypted and uncompressed bytes
|
215
|
-
# written to the file so far
|
202
|
+
# written to the file so far.
|
216
203
|
attr_reader :size
|
217
204
|
|
218
205
|
end
|
data/lib/symmetric_encryption.rb
CHANGED
@@ -13,12 +13,17 @@ module SymmetricEncryption
|
|
13
13
|
autoload :Coerce, 'symmetric_encryption/coerce'
|
14
14
|
autoload :Config, 'symmetric_encryption/config'
|
15
15
|
autoload :Encoder, 'symmetric_encryption/encoder'
|
16
|
-
autoload :
|
16
|
+
autoload :Generator, 'symmetric_encryption/generator'
|
17
|
+
autoload :Header, 'symmetric_encryption/header'
|
18
|
+
autoload :Key, 'symmetric_encryption/key'
|
17
19
|
autoload :Reader, 'symmetric_encryption/reader'
|
20
|
+
autoload :RSAKey, 'symmetric_encryption/rsa_key'
|
18
21
|
autoload :Writer, 'symmetric_encryption/writer'
|
19
|
-
autoload :
|
22
|
+
autoload :CLI, 'symmetric_encryption/cli'
|
23
|
+
autoload :Keystore, 'symmetric_encryption/keystore'
|
20
24
|
module Utils
|
21
|
-
autoload :
|
25
|
+
autoload :Generate, 'symmetric_encryption/utils/generate'
|
26
|
+
autoload :ReEncryptFiles, 'symmetric_encryption/utils/re_encrypt_files'
|
22
27
|
end
|
23
28
|
end
|
24
29
|
#@formatter:on
|
@@ -31,6 +36,6 @@ end
|
|
31
36
|
require 'symmetric_encryption/railties/symmetric_encryption_validator' if defined?(ActiveModel)
|
32
37
|
require 'symmetric_encryption/extensions/mongoid/encrypted' if defined?(Mongoid)
|
33
38
|
if defined?(MongoMapper)
|
34
|
-
warn 'MongoMapper support is deprecated.
|
39
|
+
warn 'MongoMapper support is deprecated. Upgrade to Mongoid.'
|
35
40
|
require 'symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key'
|
36
41
|
end
|
data/test/active_record_test.rb
CHANGED
@@ -77,15 +77,6 @@ class UniqueUser < ActiveRecord::Base
|
|
77
77
|
end
|
78
78
|
#@formatter:on
|
79
79
|
|
80
|
-
# Initialize the database connection
|
81
|
-
config_file = File.join(File.dirname(__FILE__), 'config', 'database.yml')
|
82
|
-
raise 'database config not found. Create a config file at: test/config/database.yml' unless File.exist? config_file
|
83
|
-
|
84
|
-
cfg = YAML.load(ERB.new(File.new(config_file).read).result)['test']
|
85
|
-
raise("Environment 'test' not defined in test/config/database.yml") unless cfg
|
86
|
-
|
87
|
-
User.establish_connection(cfg)
|
88
|
-
|
89
80
|
#
|
90
81
|
# Unit Test for attr_encrypted extensions in ActiveRecord
|
91
82
|
#
|
@@ -114,26 +105,26 @@ class ActiveRecordTest < Minitest::Test
|
|
114
105
|
|
115
106
|
@user = User.new(
|
116
107
|
# Encrypted Attribute
|
117
|
-
bank_account_number:
|
108
|
+
bank_account_number: @bank_account_number,
|
118
109
|
# Encrypted Attribute
|
119
110
|
social_security_number: @social_security_number,
|
120
111
|
name: @name,
|
121
112
|
# data type specific fields
|
122
|
-
string_value:
|
123
|
-
long_string_value:
|
124
|
-
binary_string_value:
|
125
|
-
integer_value:
|
126
|
-
float_value:
|
127
|
-
decimal_value:
|
128
|
-
datetime_value:
|
129
|
-
time_value:
|
130
|
-
date_value:
|
131
|
-
true_value:
|
132
|
-
false_value:
|
133
|
-
data_yaml:
|
134
|
-
data_json:
|
135
|
-
text:
|
136
|
-
number:
|
113
|
+
string_value: STRING_VALUE,
|
114
|
+
long_string_value: LONG_STRING_VALUE,
|
115
|
+
binary_string_value: BINARY_STRING_VALUE,
|
116
|
+
integer_value: INTEGER_VALUE,
|
117
|
+
float_value: FLOAT_VALUE,
|
118
|
+
decimal_value: DECIMAL_VALUE,
|
119
|
+
datetime_value: DATETIME_VALUE,
|
120
|
+
time_value: TIME_VALUE,
|
121
|
+
date_value: DATE_VALUE,
|
122
|
+
true_value: true,
|
123
|
+
false_value: false,
|
124
|
+
data_yaml: @h.dup,
|
125
|
+
data_json: @h.dup,
|
126
|
+
text: 'hello',
|
127
|
+
number: '21'
|
137
128
|
)
|
138
129
|
end
|
139
130
|
|
@@ -171,9 +162,9 @@ class ActiveRecordTest < Minitest::Test
|
|
171
162
|
it 'true' do
|
172
163
|
@user.string_value = STRING_VALUE
|
173
164
|
assert first_value = @user.encrypted_string_value
|
174
|
-
|
175
|
-
@user.string_value = STRING_VALUE
|
176
|
-
|
165
|
+
@user.string_value = 'blah'
|
166
|
+
@user.string_value = STRING_VALUE
|
167
|
+
refute_equal first_value, @user.encrypted_string_value
|
177
168
|
end
|
178
169
|
|
179
170
|
it 'true and compress: true' do
|
@@ -182,9 +173,48 @@ class ActiveRecordTest < Minitest::Test
|
|
182
173
|
|
183
174
|
refute_equal @user.encrypted_long_string_value, @user.encrypted_string_value
|
184
175
|
end
|
176
|
+
|
177
|
+
describe 'changed?' do
|
178
|
+
it 'true for a new instance' do
|
179
|
+
assert @user.string_value_changed?
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'clears after save' do
|
183
|
+
@user.save!
|
184
|
+
refute @user.string_value_changed?
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'does not change when equal' do
|
188
|
+
@user.save!
|
189
|
+
before = @user.encrypted_string_value
|
190
|
+
@user.string_value = STRING_VALUE
|
191
|
+
refute @user.string_value_changed?
|
192
|
+
assert_equal before, @user.encrypted_string_value
|
193
|
+
end
|
194
|
+
end
|
185
195
|
end
|
186
196
|
|
187
197
|
describe 'attribute=' do
|
198
|
+
it 'handles nil' do
|
199
|
+
@user.string_value = nil
|
200
|
+
assert_nil @user.string_value
|
201
|
+
assert_nil @user.encrypted_string_value
|
202
|
+
@user.save!
|
203
|
+
@user.reload
|
204
|
+
assert_nil @user.string_value
|
205
|
+
assert_nil @user.encrypted_string_value
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'handles empty string' do
|
209
|
+
@user.string_value = ''
|
210
|
+
assert_equal '', @user.string_value
|
211
|
+
assert_equal '', @user.encrypted_string_value
|
212
|
+
@user.save!
|
213
|
+
@user.reload
|
214
|
+
assert_equal '', @user.string_value
|
215
|
+
assert_equal '', @user.encrypted_string_value
|
216
|
+
end
|
217
|
+
|
188
218
|
it 'encrypt' do
|
189
219
|
user = User.new
|
190
220
|
user.bank_account_number = @bank_account_number
|
@@ -290,7 +320,7 @@ class ActiveRecordTest < Minitest::Test
|
|
290
320
|
assert @user.valid?
|
291
321
|
@user.number = ''
|
292
322
|
assert_equal false, @user.valid?
|
293
|
-
|
323
|
+
assert_equal '', @user.number
|
294
324
|
assert_equal ["can't be blank"], @user.errors[:number]
|
295
325
|
@user.number = nil
|
296
326
|
assert_nil @user.number
|
@@ -413,17 +443,8 @@ class ActiveRecordTest < Minitest::Test
|
|
413
443
|
@user_clone.save!
|
414
444
|
|
415
445
|
@user.reload
|
416
|
-
|
417
|
-
|
418
|
-
end
|
419
|
-
|
420
|
-
it 'permit replacing value with a blank string' do
|
421
|
-
@user_clone.send("#{@attribute}=".to_sym, ' ')
|
422
|
-
@user_clone.save!
|
423
|
-
|
424
|
-
@user.reload
|
425
|
-
assert_nil @user.send(@attribute)
|
426
|
-
assert_nil @user.send("encrypted_#{@attribute}".to_sym)
|
446
|
+
assert_equal '', @user.send(@attribute)
|
447
|
+
assert_equal '', @user.send("encrypted_#{@attribute}".to_sym)
|
427
448
|
end
|
428
449
|
|
429
450
|
it 'permit replacing value' do
|