symmetric-encryption 4.0.0 → 4.0.1
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 +5 -5
- data/Rakefile +2 -2
- data/bin/symmetric-encryption +1 -1
- data/lib/symmetric-encryption.rb +1 -1
- data/lib/symmetric_encryption.rb +2 -2
- data/lib/symmetric_encryption/cipher.rb +15 -18
- data/lib/symmetric_encryption/cli.rb +30 -36
- data/lib/symmetric_encryption/coerce.rb +3 -4
- data/lib/symmetric_encryption/config.rb +30 -34
- data/lib/symmetric_encryption/encoder.rb +0 -1
- data/lib/symmetric_encryption/exception.rb +0 -2
- data/lib/symmetric_encryption/extensions/active_record/base.rb +5 -2
- data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +3 -5
- data/lib/symmetric_encryption/extensions/mongoid/encrypted.rb +0 -2
- data/lib/symmetric_encryption/generator.rb +3 -3
- data/lib/symmetric_encryption/header.rb +9 -4
- data/lib/symmetric_encryption/key.rb +3 -4
- data/lib/symmetric_encryption/keystore.rb +9 -9
- data/lib/symmetric_encryption/keystore/environment.rb +6 -7
- data/lib/symmetric_encryption/keystore/file.rb +5 -6
- data/lib/symmetric_encryption/keystore/memory.rb +2 -2
- data/lib/symmetric_encryption/railtie.rb +4 -7
- data/lib/symmetric_encryption/railties/symmetric_encryption_validator.rb +2 -1
- data/lib/symmetric_encryption/reader.rb +28 -39
- data/lib/symmetric_encryption/symmetric_encryption.rb +10 -8
- data/lib/symmetric_encryption/utils/re_encrypt_files.rb +5 -8
- data/lib/symmetric_encryption/version.rb +2 -2
- data/lib/symmetric_encryption/writer.rb +12 -17
- data/test/active_record_test.rb +237 -200
- data/test/cipher_test.rb +12 -6
- data/test/encoder_test.rb +1 -3
- data/test/header_test.rb +0 -4
- data/test/key_test.rb +0 -2
- data/test/keystore/environment_test.rb +10 -11
- data/test/keystore/file_test.rb +9 -10
- data/test/keystore_test.rb +2 -3
- data/test/mongoid_test.rb +37 -40
- data/test/reader_test.rb +24 -32
- data/test/symmetric_encryption_test.rb +17 -18
- data/test/test_db.sqlite3 +0 -0
- data/test/writer_test.rb +0 -1
- metadata +23 -23
@@ -1,5 +1,4 @@
|
|
1
1
|
module SymmetricEncryption
|
2
|
-
|
3
2
|
# Exceptions created by SymmetricEncryption
|
4
3
|
class Error < StandardError
|
5
4
|
end
|
@@ -11,5 +10,4 @@ module SymmetricEncryption
|
|
11
10
|
# Exceptions when trying to use the keys before they have been configured
|
12
11
|
class ConfigError < Error
|
13
12
|
end
|
14
|
-
|
15
13
|
end
|
@@ -36,7 +36,11 @@ module ActiveRecord #:nodoc:
|
|
36
36
|
def self.attr_encrypted(*params)
|
37
37
|
# Ensure ActiveRecord has created all its methods first
|
38
38
|
# Ignore failures since the table may not yet actually exist
|
39
|
-
|
39
|
+
begin
|
40
|
+
define_attribute_methods
|
41
|
+
rescue StandardError
|
42
|
+
nil
|
43
|
+
end
|
40
44
|
|
41
45
|
options = params.last.is_a?(Hash) ? params.pop.dup : {}
|
42
46
|
|
@@ -102,6 +106,5 @@ module ActiveRecord #:nodoc:
|
|
102
106
|
def self.encrypted_column?(attribute)
|
103
107
|
encrypted_columns.include?(attribute)
|
104
108
|
end
|
105
|
-
|
106
109
|
end
|
107
110
|
end
|
@@ -16,15 +16,15 @@ module MongoMapper
|
|
16
16
|
Date => :date,
|
17
17
|
Boolean => :boolean,
|
18
18
|
Hash => :json
|
19
|
-
}
|
19
|
+
}.freeze
|
20
20
|
|
21
21
|
module ClassMethods
|
22
|
-
def encrypted_key(key_name, type, full_options={})
|
22
|
+
def encrypted_key(key_name, type, full_options = {})
|
23
23
|
full_options = full_options.is_a?(Hash) ? full_options.dup : {}
|
24
24
|
options = full_options.delete(:encrypted) || {}
|
25
25
|
# Support overriding the name of the decrypted attribute
|
26
26
|
encrypted_key_name = options.delete(:encrypt_as) || "encrypted_#{key_name}"
|
27
|
-
options[:type] = COERCION_MAP[type] unless [
|
27
|
+
options[:type] = COERCION_MAP[type] unless %i[yaml json].include?(options[:type])
|
28
28
|
|
29
29
|
raise(ArgumentError, "Invalid type: #{type.inspect}. Valid types: #{COERCION_MAP.keys.join(',')}") unless options[:type]
|
30
30
|
|
@@ -32,9 +32,7 @@ module MongoMapper
|
|
32
32
|
|
33
33
|
key(encrypted_key_name, String, full_options)
|
34
34
|
end
|
35
|
-
|
36
35
|
end
|
37
|
-
|
38
36
|
end
|
39
37
|
end
|
40
38
|
end
|
@@ -8,7 +8,7 @@ module SymmetricEncryption
|
|
8
8
|
compress = options.delete(:compress) || false
|
9
9
|
type = options.delete(:type) || :string
|
10
10
|
|
11
|
-
raise(ArgumentError, "SymmetricEncryption Invalid options #{options.inspect} when encrypting '#{decrypted_name}'")
|
11
|
+
raise(ArgumentError, "SymmetricEncryption Invalid options #{options.inspect} when encrypting '#{decrypted_name}'") unless options.empty?
|
12
12
|
raise(ArgumentError, "Invalid type: #{type.inspect}. Valid types: #{SymmetricEncryption::COERCION_TYPES.inspect}") unless SymmetricEncryption::COERCION_TYPES.include?(type)
|
13
13
|
|
14
14
|
if model.const_defined?(:EncryptedAttributes, _search_ancestors = false)
|
@@ -19,7 +19,7 @@ module SymmetricEncryption
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# Generate getter and setter methods
|
22
|
-
mod.module_eval(
|
22
|
+
mod.module_eval(<<~ACCESSORS, __FILE__, __LINE__ + 1)
|
23
23
|
# Set the un-encrypted field
|
24
24
|
# Also updates the encrypted field with the encrypted value
|
25
25
|
# Freeze the decrypted field value so that it is not modified directly
|
@@ -45,7 +45,7 @@ module SymmetricEncryption
|
|
45
45
|
def #{decrypted_name}_changed?
|
46
46
|
#{encrypted_name}_changed?
|
47
47
|
end
|
48
|
-
|
48
|
+
ACCESSORS
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
@@ -78,7 +78,7 @@ module SymmetricEncryption
|
|
78
78
|
auth_tag: nil)
|
79
79
|
|
80
80
|
@version = version
|
81
|
-
@compress
|
81
|
+
@compress = compress
|
82
82
|
@iv = iv
|
83
83
|
@key = key
|
84
84
|
@cipher_name = cipher_name
|
@@ -111,7 +111,7 @@ module SymmetricEncryption
|
|
111
111
|
# String to extract the header from
|
112
112
|
def parse!(buffer)
|
113
113
|
offset = parse(buffer)
|
114
|
-
return if offset
|
114
|
+
return if offset.zero?
|
115
115
|
buffer.slice!(0..offset - 1)
|
116
116
|
buffer
|
117
117
|
end
|
@@ -153,7 +153,13 @@ module SymmetricEncryption
|
|
153
153
|
self.version = buffer.getbyte(offset)
|
154
154
|
offset += 1
|
155
155
|
|
156
|
-
|
156
|
+
unless cipher
|
157
|
+
raise(
|
158
|
+
SymmetricEncryption::CipherError,
|
159
|
+
"Cipher with version:#{version.inspect} not found in any of the configured SymmetricEncryption ciphers"
|
160
|
+
)
|
161
|
+
end
|
162
|
+
|
157
163
|
flags = buffer.getbyte(offset)
|
158
164
|
offset += 1
|
159
165
|
|
@@ -255,6 +261,5 @@ module SymmetricEncryption
|
|
255
261
|
out = buffer.byteslice(offset, len)
|
256
262
|
[out, offset + len]
|
257
263
|
end
|
258
|
-
|
259
264
|
end
|
260
265
|
end
|
@@ -12,7 +12,7 @@ module SymmetricEncryption
|
|
12
12
|
# * encrypted_key
|
13
13
|
# * key_filename
|
14
14
|
def self.from_config(key: nil, key_filename: nil, encrypted_key: nil, key_env_var: nil,
|
15
|
-
|
15
|
+
iv:, key_encrypting_key: nil, cipher_name: 'aes-256-cbc')
|
16
16
|
|
17
17
|
if key_encrypting_key.is_a?(Hash)
|
18
18
|
# Recurse up the chain returning the parent key_encrypting_key
|
@@ -21,12 +21,12 @@ module SymmetricEncryption
|
|
21
21
|
|
22
22
|
key ||=
|
23
23
|
if encrypted_key
|
24
|
-
raise(ArgumentError,
|
24
|
+
raise(ArgumentError, 'Missing mandatory :key_encrypting_key when config includes :encrypted_key') unless key_encrypting_key
|
25
25
|
Keystore::Memory.new(encrypted_key: encrypted_key, key_encrypting_key: key_encrypting_key).read
|
26
26
|
elsif key_filename
|
27
27
|
Keystore::File.new(file_name: key_filename, key_encrypting_key: key_encrypting_key).read
|
28
28
|
elsif key_env_var
|
29
|
-
raise(ArgumentError,
|
29
|
+
raise(ArgumentError, 'Missing mandatory :key_encrypting_key when config includes :key_env_var') unless key_encrypting_key
|
30
30
|
Keystore::Environment.new(key_env_var: key_env_var, key_encrypting_key: key_encrypting_key).read
|
31
31
|
end
|
32
32
|
|
@@ -101,6 +101,5 @@ module SymmetricEncryption
|
|
101
101
|
result = openssl_cipher.update(encrypted_string)
|
102
102
|
result << openssl_cipher.final
|
103
103
|
end
|
104
|
-
|
105
104
|
end
|
106
105
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module SymmetricEncryption
|
2
|
+
# Encryption keys are secured in Keystores
|
2
3
|
module Keystore
|
3
|
-
|
4
|
+
# @formatter:off
|
4
5
|
autoload :Environment, 'symmetric_encryption/keystore/environment'
|
5
6
|
autoload :File, 'symmetric_encryption/keystore/file'
|
6
7
|
autoload :Memory, 'symmetric_encryption/keystore/memory'
|
7
|
-
|
8
|
+
# @formatter:on
|
8
9
|
|
9
10
|
# Returns [Hash] a new configuration file after performing key rotation.
|
10
11
|
#
|
@@ -44,12 +45,12 @@ module SymmetricEncryption
|
|
44
45
|
|
45
46
|
cipher_name = config[:cipher_name] || 'aes-256-cbc'
|
46
47
|
new_key_config =
|
47
|
-
if config.
|
48
|
+
if config.key?(:key_filename)
|
48
49
|
key_path = ::File.dirname(config[:key_filename])
|
49
50
|
Keystore::File.new_key_config(key_path: key_path, cipher_name: cipher_name, app_name: app_name, version: version, environment: environment)
|
50
|
-
elsif config.
|
51
|
+
elsif config.key?(:key_env_var)
|
51
52
|
Keystore::Environment.new_key_config(cipher_name: cipher_name, app_name: app_name, version: version, environment: environment)
|
52
|
-
elsif config.
|
53
|
+
elsif config.key?(:encrypted_key)
|
53
54
|
Keystore::Memory.new_key_config(cipher_name: cipher_name, app_name: app_name, version: version, environment: environment)
|
54
55
|
end
|
55
56
|
|
@@ -88,12 +89,12 @@ module SymmetricEncryption
|
|
88
89
|
key = Key.from_config(config)
|
89
90
|
cipher_name = key.cipher_name
|
90
91
|
new_key_config =
|
91
|
-
if config.
|
92
|
+
if config.key?(:key_filename)
|
92
93
|
key_path = ::File.dirname(config[:key_filename])
|
93
94
|
Keystore::File.new_key_config(key_path: key_path, cipher_name: cipher_name, app_name: app_name, version: version, environment: environment, dek: key)
|
94
|
-
elsif config.
|
95
|
+
elsif config.key?(:key_env_var)
|
95
96
|
Keystore::Environment.new_key_config(cipher_name: cipher_name, app_name: app_name, version: version, environment: environment, dek: key)
|
96
|
-
elsif config.
|
97
|
+
elsif config.key?(:encrypted_key)
|
97
98
|
Keystore::Memory.new_key_config(cipher_name: cipher_name, app_name: app_name, version: version, environment: environment, dek: key)
|
98
99
|
end
|
99
100
|
|
@@ -121,6 +122,5 @@ module SymmetricEncryption
|
|
121
122
|
]
|
122
123
|
}
|
123
124
|
end
|
124
|
-
|
125
125
|
end
|
126
126
|
end
|
@@ -7,14 +7,14 @@ module SymmetricEncryption
|
|
7
7
|
# Returns [Hash] initial configuration for heroku.
|
8
8
|
# Displays the keys that need to be added to the heroku environment.
|
9
9
|
def self.new_config(app_name: 'symmetric-encryption',
|
10
|
-
|
11
|
-
|
10
|
+
environments: %i[development test release production],
|
11
|
+
cipher_name: 'aes-256-cbc')
|
12
12
|
|
13
13
|
configs = {}
|
14
14
|
environments.each do |environment|
|
15
15
|
environment = environment.to_sym
|
16
16
|
configs[environment] =
|
17
|
-
if %i
|
17
|
+
if %i[development test].include?(environment)
|
18
18
|
Keystore.dev_config
|
19
19
|
else
|
20
20
|
cfg = new_key_config(cipher_name: cipher_name, app_name: app_name, environment: environment)
|
@@ -35,7 +35,7 @@ module SymmetricEncryption
|
|
35
35
|
kek = SymmetricEncryption::Key.new(cipher_name: cipher_name)
|
36
36
|
dek ||= SymmetricEncryption::Key.new(cipher_name: cipher_name)
|
37
37
|
|
38
|
-
key_env_var = "#{app_name}_#{environment}_v#{version}".upcase.
|
38
|
+
key_env_var = "#{app_name}_#{environment}_v#{version}".upcase.tr('-', '_')
|
39
39
|
new(key_env_var: key_env_var, key_encrypting_key: kek).write(dek.key)
|
40
40
|
|
41
41
|
{
|
@@ -45,7 +45,7 @@ module SymmetricEncryption
|
|
45
45
|
iv: dek.iv,
|
46
46
|
key_encrypting_key: {
|
47
47
|
key: kek.key,
|
48
|
-
iv: kek.iv
|
48
|
+
iv: kek.iv
|
49
49
|
}
|
50
50
|
}
|
51
51
|
end
|
@@ -75,7 +75,7 @@ module SymmetricEncryption
|
|
75
75
|
puts
|
76
76
|
puts "Or, if using environment variables on another system set the environment variable as follows:\n\n"
|
77
77
|
puts " export #{key_env_var}=\"#{encoder.encode(encrypted_key)}\"\n\n"
|
78
|
-
puts
|
78
|
+
puts '********************************************************************************'
|
79
79
|
end
|
80
80
|
|
81
81
|
private
|
@@ -84,7 +84,6 @@ module SymmetricEncryption
|
|
84
84
|
def encoder
|
85
85
|
@encoder ||= SymmetricEncryption::Encoder[encoding]
|
86
86
|
end
|
87
|
-
|
88
87
|
end
|
89
88
|
end
|
90
89
|
end
|
@@ -6,15 +6,15 @@ module SymmetricEncryption
|
|
6
6
|
# Returns [Hash] initial configuration.
|
7
7
|
# Generates the encrypted key file for every environment except development and test.
|
8
8
|
def self.new_config(key_path: '/etc/symmetric-encryption',
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
app_name: 'symmetric-encryption',
|
10
|
+
environments: %i[development test release production],
|
11
|
+
cipher_name: 'aes-256-cbc')
|
12
12
|
|
13
13
|
configs = {}
|
14
14
|
environments.each do |environment|
|
15
15
|
environment = environment.to_sym
|
16
16
|
configs[environment] =
|
17
|
-
if %i
|
17
|
+
if %i[development test].include?(environment)
|
18
18
|
Keystore.dev_config
|
19
19
|
else
|
20
20
|
cfg = new_key_config(key_path: key_path, cipher_name: cipher_name, app_name: app_name, environment: environment)
|
@@ -84,7 +84,7 @@ module SymmetricEncryption
|
|
84
84
|
|
85
85
|
# Read from the file, raising an exception if it is not found
|
86
86
|
def read_from_file
|
87
|
-
::File.open(file_name, 'rb'
|
87
|
+
::File.open(file_name, 'rb', &:read)
|
88
88
|
rescue Errno::ENOENT
|
89
89
|
raise(SymmetricEncryption::ConfigError, "Symmetric Encryption key file: '#{file_name}' not found or readable")
|
90
90
|
end
|
@@ -96,7 +96,6 @@ module SymmetricEncryption
|
|
96
96
|
::File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if ::File.exist?(file_name)
|
97
97
|
::File.open(file_name, 'wb') { |file| file.write(data) }
|
98
98
|
end
|
99
|
-
|
100
99
|
end
|
101
100
|
end
|
102
101
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module SymmetricEncryption
|
2
2
|
module Keystore
|
3
|
+
# In Memory Keystore usually used for testing purposes
|
3
4
|
class Memory
|
4
5
|
attr_accessor :key_encrypting_key
|
5
6
|
attr_reader :encrypted_key
|
@@ -26,7 +27,7 @@ module SymmetricEncryption
|
|
26
27
|
iv: iv,
|
27
28
|
key_encrypting_key: {
|
28
29
|
key: kek.key,
|
29
|
-
iv: kek.iv
|
30
|
+
iv: kek.iv
|
30
31
|
}
|
31
32
|
}
|
32
33
|
end
|
@@ -47,7 +48,6 @@ module SymmetricEncryption
|
|
47
48
|
def write(key)
|
48
49
|
self.encrypted_key = key_encrypting_key.encrypt(key)
|
49
50
|
end
|
50
|
-
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -1,7 +1,5 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
module SymmetricEncryption #:nodoc:
|
3
2
|
class Railtie < Rails::Railtie #:nodoc:
|
4
|
-
|
5
3
|
# Exposes Symmetric Encryption's configuration to the Rails application configuration.
|
6
4
|
#
|
7
5
|
# @example Set up configuration in the Rails app.
|
@@ -31,23 +29,22 @@ module SymmetricEncryption #:nodoc:
|
|
31
29
|
config.before_configuration do
|
32
30
|
# Check if already configured
|
33
31
|
unless ::SymmetricEncryption.cipher?
|
34
|
-
app_name
|
32
|
+
app_name = Rails::Application.subclasses.first.parent.to_s.underscore
|
35
33
|
config_file = Rails.root.join('config', 'symmetric-encryption.yml')
|
36
34
|
if config_file.file?
|
37
35
|
begin
|
38
|
-
::SymmetricEncryption::Config.load!(file_name: config_file, env: Rails.env)
|
36
|
+
::SymmetricEncryption::Config.load!(file_name: config_file, env: ENV['SYMMETRIC_ENCRYPTION_ENV'] || Rails.env)
|
39
37
|
rescue ArgumentError => exc
|
40
38
|
puts "\nSymmetric Encryption not able to read keys."
|
41
39
|
puts "#{exc.class.name} #{exc.message}"
|
42
|
-
puts "To generate a new config file and key files: symmetric-encryption --generate --key-path /etc/#{app_name} --
|
40
|
+
puts "To generate a new config file and key files: symmetric-encryption --generate --key-path /etc/#{app_name} --app-name #{app_name}\n\n"
|
43
41
|
raise(exc)
|
44
42
|
end
|
45
43
|
else
|
46
44
|
puts "\nSymmetric Encryption config not found."
|
47
|
-
puts "To generate a new config file and key files: symmetric-encryption --generate --key-path /etc/#{app_name} --
|
45
|
+
puts "To generate a new config file and key files: symmetric-encryption --generate --key-path /etc/#{app_name} --app-name #{app_name}\n\n"
|
48
46
|
end
|
49
47
|
end
|
50
48
|
end
|
51
|
-
|
52
49
|
end
|
53
50
|
end
|
@@ -13,6 +13,7 @@
|
|
13
13
|
# # => true
|
14
14
|
class SymmetricEncryptionValidator < ActiveModel::EachValidator
|
15
15
|
def validate_each(record, attribute, value)
|
16
|
-
|
16
|
+
return if value.blank? || SymmetricEncryption.encrypted?(value)
|
17
|
+
record.errors.add(attribute, 'must be a value encrypted using SymmetricEncryption.encrypt')
|
17
18
|
end
|
18
19
|
end
|
@@ -59,11 +59,11 @@ module SymmetricEncryption
|
|
59
59
|
# ensure
|
60
60
|
# csv.close if csv
|
61
61
|
# end
|
62
|
-
def self.open(file_name_or_stream, buffer_size:
|
62
|
+
def self.open(file_name_or_stream, buffer_size: 16_384, **args, &block)
|
63
63
|
ios = file_name_or_stream.is_a?(String) ? ::File.open(file_name_or_stream, 'rb') : file_name_or_stream
|
64
64
|
|
65
65
|
begin
|
66
|
-
file =
|
66
|
+
file = new(ios, buffer_size: buffer_size, **args)
|
67
67
|
file = Zlib::GzipReader.new(file) if !file.eof? && file.compressed?
|
68
68
|
block ? block.call(file) : file
|
69
69
|
ensure
|
@@ -76,7 +76,7 @@ module SymmetricEncryption
|
|
76
76
|
# Notes:
|
77
77
|
# * Do not use this method for reading large files.
|
78
78
|
def self.read(file_name_or_stream, **args)
|
79
|
-
open(file_name_or_stream, **args
|
79
|
+
self.open(file_name_or_stream, **args, &:read)
|
80
80
|
end
|
81
81
|
|
82
82
|
# Decrypt an entire file.
|
@@ -97,23 +97,21 @@ module SymmetricEncryption
|
|
97
97
|
#
|
98
98
|
# Notes:
|
99
99
|
# * The file contents are streamed so that the entire file is _not_ loaded into memory.
|
100
|
-
def self.decrypt(source:, target:, block_size:
|
100
|
+
def self.decrypt(source:, target:, block_size: 65_535, **args)
|
101
101
|
target_ios = target.is_a?(String) ? ::File.open(target, 'wb') : target
|
102
102
|
bytes_written = 0
|
103
|
-
open(source, **args) do |input_ios|
|
104
|
-
|
105
|
-
bytes_written += target_ios.write(input_ios.read(block_size))
|
106
|
-
end
|
103
|
+
self.open(source, **args) do |input_ios|
|
104
|
+
bytes_written += target_ios.write(input_ios.read(block_size)) until input_ios.eof?
|
107
105
|
end
|
108
106
|
bytes_written
|
109
107
|
ensure
|
110
|
-
target_ios.close if target_ios
|
108
|
+
target_ios.close if target_ios&.respond_to?(:closed?) && !target_ios.closed?
|
111
109
|
end
|
112
110
|
|
113
111
|
# Returns [true|false] whether the file or stream contains any data
|
114
112
|
# excluding the header should it have one
|
115
113
|
def self.empty?(file_name_or_stream)
|
116
|
-
open(file_name_or_stream
|
114
|
+
open(file_name_or_stream, &:eof?)
|
117
115
|
end
|
118
116
|
|
119
117
|
# Returns [true|false] whether the file contains the encryption header
|
@@ -161,9 +159,7 @@ module SymmetricEncryption
|
|
161
159
|
#
|
162
160
|
# Note: When no header is present, the version is set to the one supplied
|
163
161
|
# in the options
|
164
|
-
|
165
|
-
@version
|
166
|
-
end
|
162
|
+
attr_reader :version
|
167
163
|
|
168
164
|
# Close the IO Stream
|
169
165
|
#
|
@@ -198,19 +194,17 @@ module SymmetricEncryption
|
|
198
194
|
#
|
199
195
|
# At end of file, it returns nil if no more data is available, or the last
|
200
196
|
# remaining bytes
|
201
|
-
def read(length=nil)
|
197
|
+
def read(length = nil)
|
202
198
|
data = nil
|
203
199
|
if length
|
204
|
-
return '' if length
|
200
|
+
return '' if length.zero?
|
205
201
|
return nil if eof?
|
206
202
|
# Read length bytes
|
207
|
-
while (@read_buffer.length < length) && !@ios.eof?
|
208
|
-
|
209
|
-
end
|
210
|
-
if @read_buffer.length == 0
|
203
|
+
read_block while (@read_buffer.length < length) && !@ios.eof?
|
204
|
+
if @read_buffer.empty?
|
211
205
|
data = nil
|
212
206
|
elsif @read_buffer.length > length
|
213
|
-
data = @read_buffer.slice!(0..length-1)
|
207
|
+
data = @read_buffer.slice!(0..length - 1)
|
214
208
|
else
|
215
209
|
data = @read_buffer
|
216
210
|
@read_buffer = ''
|
@@ -220,10 +214,10 @@ module SymmetricEncryption
|
|
220
214
|
data = @read_buffer
|
221
215
|
@read_buffer = ''
|
222
216
|
|
223
|
-
|
217
|
+
unless @ios.eof?
|
224
218
|
# Read entire file
|
225
219
|
buf = @ios.read || ''
|
226
|
-
data << @stream_cipher.update(buf) if buf && buf.
|
220
|
+
data << @stream_cipher.update(buf) if buf && !buf.empty?
|
227
221
|
data << @stream_cipher.final
|
228
222
|
end
|
229
223
|
end
|
@@ -235,14 +229,14 @@ module SymmetricEncryption
|
|
235
229
|
# Raises EOFError on eof
|
236
230
|
# The stream must be opened for reading or an IOError will be raised.
|
237
231
|
def readline(sep_string = "\n")
|
238
|
-
gets(sep_string) || raise(EOFError
|
232
|
+
gets(sep_string) || raise(EOFError, 'End of file reached when trying to read a line')
|
239
233
|
end
|
240
234
|
|
241
235
|
# Reads a single decrypted line from the file up to and including the optional sep_string.
|
242
236
|
# A sep_string of nil reads the entire contents of the file
|
243
237
|
# Returns nil on eof
|
244
238
|
# The stream must be opened for reading or an IOError will be raised.
|
245
|
-
def gets(sep_string, length=nil)
|
239
|
+
def gets(sep_string, length = nil)
|
246
240
|
return read(length) if sep_string.nil?
|
247
241
|
|
248
242
|
# Read more data until we get the sep_string
|
@@ -253,7 +247,7 @@ module SymmetricEncryption
|
|
253
247
|
index ||= -1
|
254
248
|
data = @read_buffer.slice!(0..index)
|
255
249
|
@pos += data.length
|
256
|
-
return nil if data.
|
250
|
+
return nil if data.empty? && eof?
|
257
251
|
data
|
258
252
|
end
|
259
253
|
|
@@ -262,23 +256,19 @@ module SymmetricEncryption
|
|
262
256
|
# Executes the block for every line in ios, where lines are separated by sep_string.
|
263
257
|
# ios must be opened for reading or an IOError will be raised.
|
264
258
|
def each_line(sep_string = "\n")
|
265
|
-
|
266
|
-
yield gets(sep_string)
|
267
|
-
end
|
259
|
+
yield gets(sep_string) until eof?
|
268
260
|
self
|
269
261
|
end
|
270
262
|
|
271
|
-
|
263
|
+
alias each each_line
|
272
264
|
|
273
265
|
# Returns whether the end of file has been reached for this stream
|
274
266
|
def eof?
|
275
|
-
|
267
|
+
@read_buffer.empty? && @ios.eof?
|
276
268
|
end
|
277
269
|
|
278
270
|
# Return the number of bytes read so far from the input stream
|
279
|
-
|
280
|
-
@pos
|
281
|
-
end
|
271
|
+
attr_reader :pos
|
282
272
|
|
283
273
|
# Rewind back to the beginning of the file
|
284
274
|
def rewind
|
@@ -299,7 +289,7 @@ module SymmetricEncryption
|
|
299
289
|
# then re-read upto the point specified
|
300
290
|
# WARNING: IO::SEEK_END will read the entire file and then again
|
301
291
|
# upto the point specified
|
302
|
-
def seek(amount, whence=IO::SEEK_SET)
|
292
|
+
def seek(amount, whence = IO::SEEK_SET)
|
303
293
|
offset = 0
|
304
294
|
case whence
|
305
295
|
when IO::SEEK_SET
|
@@ -317,7 +307,7 @@ module SymmetricEncryption
|
|
317
307
|
# Read and decrypt entire file a block at a time to get its total
|
318
308
|
# unencrypted size
|
319
309
|
size = 0
|
320
|
-
|
310
|
+
until eof
|
321
311
|
read_block
|
322
312
|
size += @read_buffer.size
|
323
313
|
@read_buffer = ''
|
@@ -327,7 +317,7 @@ module SymmetricEncryption
|
|
327
317
|
else
|
328
318
|
raise(ArgumentError, "unknown whence:#{whence} supplied to seek()")
|
329
319
|
end
|
330
|
-
read(offset) if offset
|
320
|
+
read(offset) if offset.positive?
|
331
321
|
0
|
332
322
|
end
|
333
323
|
|
@@ -364,7 +354,7 @@ module SymmetricEncryption
|
|
364
354
|
@stream_cipher.iv = iv || cipher.iv
|
365
355
|
|
366
356
|
# First call to #update should return an empty string anyway
|
367
|
-
if buf && buf.
|
357
|
+
if buf && !buf.empty?
|
368
358
|
@read_buffer = @stream_cipher.update(buf)
|
369
359
|
@read_buffer << @stream_cipher.final if @ios.eof?
|
370
360
|
else
|
@@ -375,13 +365,12 @@ module SymmetricEncryption
|
|
375
365
|
# Read a block of data and append the decrypted data in the read buffer
|
376
366
|
def read_block
|
377
367
|
buf = @ios.read(@buffer_size)
|
378
|
-
@read_buffer << @stream_cipher.update(buf) if buf && buf.
|
368
|
+
@read_buffer << @stream_cipher.update(buf) if buf && !buf.empty?
|
379
369
|
@read_buffer << @stream_cipher.final if @ios.eof?
|
380
370
|
end
|
381
371
|
|
382
372
|
def closed?
|
383
373
|
@closed || @ios.respond_to?(:closed?) && @ios.closed?
|
384
374
|
end
|
385
|
-
|
386
375
|
end
|
387
376
|
end
|