symmetric-encryption 3.9.1 → 4.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|