symmetric-encryption 0.4.0 → 0.5.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.
data/README.md CHANGED
@@ -50,27 +50,31 @@ names with encrypted_
50
50
  * More efficient replacement for attr_encrypted since only ActiveRecord Models
51
51
  are extended with encrypted_ behavior, rather than every object in the system
52
52
  * Custom validator for ActiveRecord Models
53
+ * Stream based encryption and decryption so that large files can be read or
54
+ written with encryption
55
+ * Stream based encryption and decryption also supports compression and decompression
56
+ on the fly
53
57
 
54
58
  ## Examples
55
59
 
56
60
  ### Encryption Example
57
61
 
58
- Symmetric::Encryption.encrypt "Sensitive data"
62
+ SymmetricEncryption.encrypt "Sensitive data"
59
63
 
60
64
  ### Decryption Example
61
65
 
62
- Symmetric::Encryption.decrypt "JqLJOi6dNjWI9kX9lSL1XQ==\n"
66
+ SymmetricEncryption.decrypt "JqLJOi6dNjWI9kX9lSL1XQ==\n"
63
67
 
64
68
  ### Validation Example
65
69
 
66
70
  class MyModel < ActiveRecord::Base
67
- validates :encrypted_ssn, :symmetric_encrypted => true
71
+ validates :encrypted_ssn, :symmetric_encryption => true
68
72
  end
69
73
 
70
74
  m = MyModel.new
71
75
  m.valid?
72
76
  # => false
73
- m.encrypted_ssn = Symmetric::Encryption.encrypt('123456789')
77
+ m.encrypted_ssn = SymmetricEncryption.encrypt('123456789')
74
78
  m.valid?
75
79
  # => true
76
80
 
@@ -85,9 +89,9 @@ For example config/database.yml
85
89
  host: db1w
86
90
  database: myapp_production
87
91
  username: admin
88
- password: <%= Symmetric::Encryption.try_decrypt "JqLJOi6dNjWI9kX9lSL1XQ==\n" %>
92
+ password: <%= SymmetricEncryption.try_decrypt "JqLJOi6dNjWI9kX9lSL1XQ==\n" %>
89
93
 
90
- Note: Use Symmetric::Encryption.try_decrypt method which will return nil if it
94
+ Note: Use SymmetricEncryption.try_decrypt method which will return nil if it
91
95
  fails to decrypt the value, which is essential when the encryption keys differ
92
96
  between environments
93
97
 
@@ -100,6 +104,32 @@ Note: In order for the above technique to work in other YAML configuration files
100
104
  cfg = YAML.load(ERB.new(File.new(config_file).read).result)[Rails.env]
101
105
  raise("Environment #{Rails.env} not defined in redis.yml") unless cfg
102
106
 
107
+ ### Large File Encryption
108
+
109
+ Example: Read and decrypt a line at a time from a file
110
+
111
+ SymmetricEncryption::Reader.open('encrypted_file') do |file|
112
+ file.each_line do |line|
113
+ puts line
114
+ end
115
+ end
116
+
117
+ Example: Encrypt and write data to a file
118
+
119
+ SymmetricEncryption::Writer.open('encrypted_file') do |file|
120
+ file.write "Hello World\n"
121
+ file.write "Keep this secret"
122
+ end
123
+
124
+ Example: Compress, Encrypt and write data to a file
125
+
126
+ SymmetricEncryption::Writer.open('encrypted_compressed.zip', :compress => true) do |file|
127
+ file.write "Hello World\n"
128
+ file.write "Compress this\n"
129
+ file.write "Keep this safe and secure\n"
130
+ end
131
+
132
+
103
133
  ### Generating encrypted passwords
104
134
 
105
135
  The following rake task can be used to generate encrypted passwords for the
@@ -236,7 +266,7 @@ startup, run the code below to initialize symmetric-encryption prior to
236
266
  attempting to encrypt or decrypt any data
237
267
 
238
268
  require 'symmetric-encryption'
239
- Symmetric::Encryption.load!('config/symmetric-encryption.yml', 'production')
269
+ SymmetricEncryption.load!('config/symmetric-encryption.yml', 'production')
240
270
 
241
271
  Parameters:
242
272
 
@@ -246,7 +276,7 @@ Parameters:
246
276
  To manually generate the symmetric encryption keys, run the code below
247
277
 
248
278
  require 'symmetric-encryption'
249
- Symmetric::Encryption.generate_symmetric_key_files('config/symmetric-encryption.yml', 'production')
279
+ SymmetricEncryption.generate_symmetric_key_files('config/symmetric-encryption.yml', 'production')
250
280
 
251
281
  Parameters:
252
282
 
@@ -358,8 +388,8 @@ Create a configuration file in config/symmetric-encryption.yml per the following
358
388
  Submit an issue ticket to request any of the following features:
359
389
 
360
390
  * Ability to entirely disable encryption for a specific environment.
361
- Symmetric::Encryption.encrypt() would return the supplied data without encrypting it and
362
- Symmetric::Encryption.decrypt() would return the supplied data without decrypting it
391
+ SymmetricEncryption.encrypt() would return the supplied data without encrypting it and
392
+ SymmetricEncryption.decrypt() would return the supplied data without decrypting it
363
393
 
364
394
  * Support for automatically compressing data prior to encrypting it when the
365
395
  data exceeds some predefined size. And automatically decompressing the data
@@ -371,7 +401,7 @@ Submit an issue ticket to request any of the following features:
371
401
  * Create rake task / generator to generate a sample configuration file
372
402
  with a new RSA Private key already in it
373
403
 
374
- * Ability to change Symmetric::Encryption configuration options from custom
404
+ * Ability to change SymmetricEncryption configuration options from custom
375
405
  Rails initializers, rather than having everything in the config file.
376
406
  For example config.symmetric_encryption.cipher = 'aes-128-cbc'
377
407
 
data/Rakefile CHANGED
@@ -4,20 +4,20 @@ $:.unshift lib unless $:.include?(lib)
4
4
  require 'rake/clean'
5
5
  require 'rake/testtask'
6
6
  require 'date'
7
- require 'symmetric/version'
7
+ require 'symmetric_encryption/version'
8
8
 
9
9
  desc "Build gem"
10
10
  task :gem do |t|
11
11
  gemspec = Gem::Specification.new do |s|
12
12
  s.name = 'symmetric-encryption'
13
- s.version = Symmetric::VERSION
13
+ s.version = SymmetricEncryption::VERSION
14
14
  s.platform = Gem::Platform::RUBY
15
15
  s.authors = ['Reid Morrison']
16
16
  s.email = ['reidmo@gmail.com']
17
17
  s.homepage = 'https://github.com/ClarityServices/symmetric-encryption'
18
18
  s.date = Date.today.to_s
19
19
  s.summary = "Symmetric Encryption for Ruby, and Ruby on Rails"
20
- s.description = "Symmetric Encryption is a library to seamlessly enable symmetric encryption in a project, written in Ruby."
20
+ s.description = "SymmetricEncryption supports encrypting ActiveRecord data, Mongoid data, passwords in configuration files, encrypting and decrypting of large files through streaming"
21
21
  s.files = FileList["./**/*"].exclude('*.gem', 'nbproject').map{|f| f.sub(/^\.\//, '')}
22
22
  s.has_rdoc = true
23
23
  end
@@ -1,16 +1,19 @@
1
- require 'symmetric/version'
2
- require 'symmetric/cipher'
3
- require 'symmetric/encryption'
1
+ require 'symmetric_encryption/version'
2
+ require 'symmetric_encryption/cipher'
3
+ require 'symmetric_encryption/symmetric_encryption'
4
+ require 'symmetric_encryption/reader'
5
+ require 'symmetric_encryption/writer'
6
+ require 'zlib'
4
7
  if defined?(Rails)
5
- require 'symmetric/railtie'
8
+ require 'symmetric_encryption/railtie'
6
9
  end
7
10
  # attr_encrypted and Encrypted validator
8
11
  if defined?(ActiveRecord::Base)
9
- require 'symmetric/extensions/active_record/base'
10
- require 'symmetric/railties/symmetric_encrypted_validator'
12
+ require 'symmetric_encryption/extensions/active_record/base'
13
+ require 'symmetric_encryption/railties/symmetric_encryption_validator'
11
14
  end
12
15
 
13
16
  # field encryption for Mongoid
14
17
  if defined?(Mongoid)
15
- require 'symmetric/extensions/mongoid/fields'
18
+ require 'symmetric_encryption/extensions/mongoid/fields'
16
19
  end
@@ -0,0 +1,114 @@
1
+ module SymmetricEncryption
2
+
3
+ # Hold all information related to encryption keys
4
+ # as well as encrypt and decrypt data using those keys
5
+ #
6
+ # Cipher is thread safe so that the same instance can be called by multiple
7
+ # threads at the same time without needing an instance of Cipher per thread
8
+ class Cipher
9
+ # Cipher to use for encryption and decryption
10
+ attr_reader :cipher, :version
11
+
12
+ # Future Use:
13
+ # attr_accessor :encoding, :version
14
+
15
+ # Generate a new Symmetric Key pair
16
+ #
17
+ # Returns a hash containing a new random symmetric_key pair
18
+ # consisting of a :key and :iv.
19
+ # The cipher is also included for compatibility with the Cipher initializer
20
+ def self.random_key_pair(cipher = 'aes-256-cbc', generate_iv = true)
21
+ openssl_cipher = OpenSSL::Cipher.new(cipher)
22
+ openssl_cipher.encrypt
23
+
24
+ {
25
+ :key => openssl_cipher.random_key,
26
+ :iv => generate_iv ? openssl_cipher.random_iv : nil,
27
+ :cipher => cipher
28
+ }
29
+ end
30
+
31
+ # Create a Symmetric::Key for encryption and decryption purposes
32
+ #
33
+ # Parameters:
34
+ # :key
35
+ # The Symmetric Key to use for encryption and decryption
36
+ # :iv
37
+ # Optional. The Initialization Vector to use with Symmetric Key
38
+ # :cipher
39
+ # Optional. Encryption Cipher to use
40
+ # Default: aes-256-cbc
41
+ def initialize(parms={})
42
+ raise "Missing mandatory parameter :key" unless @key = parms[:key]
43
+ @iv = parms[:iv]
44
+ @cipher = parms[:cipher] || 'aes-256-cbc'
45
+ @version = parms[:version]
46
+ end
47
+
48
+ # AES Symmetric Encryption of supplied string
49
+ # Returns result as a Base64 encoded string
50
+ # Returns nil if the supplied str is nil
51
+ # Returns "" if it is a string and it is empty
52
+ #
53
+ # options:
54
+ # :encoding
55
+ # :base64 Return as a base64 encoded string
56
+ # :binary Return as raw binary data string. Note: String can contain embedded nulls
57
+ # Default: :base64
58
+ # :compress
59
+ # [true|false] Whether or not to compress the data _before_ encrypting
60
+ # Default: false
61
+ def encrypt(str)
62
+ return if str.nil?
63
+ buf = str.to_s
64
+ return str if buf.empty?
65
+ crypt(:encrypt, buf)
66
+ end
67
+
68
+ # AES Symmetric Decryption of supplied string
69
+ # Returns decrypted string
70
+ # Returns nil if the supplied str is nil
71
+ # Returns "" if it is a string and it is empty
72
+ def decrypt(str)
73
+ return if str.nil?
74
+ buf = str.to_s
75
+ return str if buf.empty?
76
+ crypt(:decrypt, buf)
77
+ end
78
+
79
+ # Return a new random key using the configured cipher
80
+ # Useful for generating new symmetric keys
81
+ def random_key
82
+ ::OpenSSL::Cipher::Cipher.new(@cipher).random_key
83
+ end
84
+
85
+ # Returns the block size for the configured cipher
86
+ def block_size
87
+ ::OpenSSL::Cipher::Cipher.new(@cipher).block_size
88
+ end
89
+
90
+ protected
91
+
92
+ # Only for use by Symmetric::EncryptedStream
93
+ def openssl_cipher(cipher_method)
94
+ openssl_cipher = ::OpenSSL::Cipher.new(self.cipher)
95
+ openssl_cipher.send(cipher_method)
96
+ openssl_cipher.key = @key
97
+ openssl_cipher.iv = @iv if @iv
98
+ openssl_cipher
99
+ end
100
+
101
+ # Creates a new OpenSSL::Cipher with every call so that this call
102
+ # is thread-safe
103
+ def crypt(cipher_method, string) #:nodoc:
104
+ openssl_cipher = ::OpenSSL::Cipher.new(self.cipher)
105
+ openssl_cipher.send(cipher_method)
106
+ openssl_cipher.key = @key
107
+ openssl_cipher.iv = @iv if @iv
108
+ result = openssl_cipher.update(string)
109
+ result << openssl_cipher.final
110
+ end
111
+
112
+ end
113
+
114
+ end
@@ -3,7 +3,7 @@ module ActiveRecord #:nodoc:
3
3
 
4
4
  class << self # Class methods
5
5
  # Much lighter weight encryption for Rails attributes matching the
6
- # attr_encrypted interface using Symmetry::Encryption
6
+ # attr_encrypted interface using SymmetricEncryption
7
7
  #
8
8
  # The regular attr_encrypted gem uses Encryptor that adds encryption to
9
9
  # every Ruby object which is a complete overkill for this simple use-case
@@ -31,7 +31,7 @@ module ActiveRecord #:nodoc:
31
31
  # If this method is not called, then the encrypted value is never decrypted
32
32
  def #{attribute}
33
33
  if @stored_encrypted_#{attribute} != self.encrypted_#{attribute}
34
- @#{attribute} = ::Symmetric::Encryption.decrypt(self.encrypted_#{attribute})
34
+ @#{attribute} = ::SymmetricEncryption.decrypt(self.encrypted_#{attribute})
35
35
  @stored_encrypted_#{attribute} = self.encrypted_#{attribute}
36
36
  end
37
37
  @#{attribute}
@@ -40,7 +40,7 @@ module ActiveRecord #:nodoc:
40
40
  # Set the un-encrypted attribute
41
41
  # Also updates the encrypted field with the encrypted value
42
42
  def #{attribute}=(value)
43
- self.encrypted_#{attribute} = @stored_encrypted_#{attribute} = ::Symmetric::Encryption.encrypt(value#{".to_yaml" if options[:marshal]})
43
+ self.encrypted_#{attribute} = @stored_encrypted_#{attribute} = ::SymmetricEncryption.encrypt(value#{".to_yaml" if options[:marshal]})
44
44
  @#{attribute} = value
45
45
  end
46
46
  UNENCRYPTED
@@ -129,7 +129,7 @@ module ActiveRecord #:nodoc:
129
129
  attribute_names.each_with_index do |attribute, index|
130
130
  encrypted_name = "encrypted_#{attribute}"
131
131
  if instance_methods.include? encrypted_name #.to_sym in 1.9
132
- args[index] = ::Symmetric::Encryption.encrypt(args[index])
132
+ args[index] = ::SymmetricEncryption.encrypt(args[index])
133
133
  attribute_names[index] = encrypted_name
134
134
  end
135
135
  end
@@ -139,9 +139,6 @@ module ActiveRecord #:nodoc:
139
139
  end
140
140
 
141
141
  alias_method_chain :method_missing, :attr_encrypted
142
- #Equivalent to:
143
- # alias_method :method_missing_without_attr_encrypted, :attr_encrypted # new, old
144
- # alias_method :attr_encrypted, :method_missing_with_attr_encrypted
145
142
 
146
143
  end
147
144
  end
@@ -9,12 +9,21 @@ module Mongoid
9
9
  module ClassMethods
10
10
  # Example:
11
11
  #
12
+ # require 'mongoid'
13
+ # require 'symmetric-encryption'
14
+ #
15
+ # # Initialize Mongoid in a standalone environment. In a Rails app this is not required
16
+ # Mongoid.logger = Logger.new($stdout)
17
+ # Mongoid.load!('config/mongoid.yml')
18
+ #
19
+ # # Initialize SymmetricEncryption in a standalone environment. In a Rails app this is not required
20
+ # SymmetricEncryption.load!('config/symmetric-encryption.yml', 'test')
21
+ #
12
22
  # class Person
13
23
  # include Mongoid::Document
14
- # include Symmetric::Encryption::Mongoid
15
24
  #
16
25
  # field :name, :type => String
17
- # field :encrypted_social_security_number, :type => String, :encrypted => true, :decrypt_as => :social_security_number
26
+ # field :encrypted_social_security_number, :type => String, :encrypted => true
18
27
  # field :age, :type => Integer
19
28
  #
20
29
  # end
@@ -35,13 +44,13 @@ module Mongoid
35
44
  # puts "Decrypted Social Security Number is: #{person.social_security_number}"
36
45
  #
37
46
  # # Or is the same as
38
- # puts "Decrypted Social Security Number is: #{Symmetric::Encryption.decrypt(person.encrypted_social_security_number)}"
47
+ # puts "Decrypted Social Security Number is: #{SymmetricEncryption.decrypt(person.encrypted_social_security_number)}"
39
48
  #
40
49
  # # Sets the encrypted_social_security_number to encrypted version
41
50
  # person.social_security_number = "123456789"
42
51
  #
43
52
  # # Or, is equivalent to:
44
- # person.social_security_number = Symmetric::Encryption.encrypt("123456789")
53
+ # person.social_security_number = SymmetricEncryption.encrypt("123456789")
45
54
  #
46
55
  #
47
56
  # Note: Unlike attr_encrypted finders must use the encrypted field name
@@ -58,10 +67,10 @@ module Mongoid
58
67
  # @param [ Symbol ] name The name of the field.
59
68
  # @param [ Hash ] options The options to pass to the field.
60
69
  #
61
- # @option options [ Class ] :type The type of the field.
62
- # @option options [ String ] :label The label for the field.
63
70
  # @option options [ Boolean ] :encryption If the field contains encrypted data.
64
71
  # @option options [ Symbol ] :decrypt_as Name of the getters and setters to generate to access the decrypted value of this field.
72
+ # @option options [ Class ] :type The type of the field.
73
+ # @option options [ String ] :label The label for the field.
65
74
  # @option options [ Object, Proc ] :default The field's default
66
75
  #
67
76
  # @return [ Field ] The generated field
@@ -69,7 +78,7 @@ module Mongoid
69
78
  if options.delete(:encrypted) == true
70
79
  decrypt_as = options.delete(:decrypt_as)
71
80
  unless decrypt_as
72
- raise "Symmetric::Encryption for Mongoid. When encryption is enabled for a field it must either start with 'encrypted_' or the option :decrypt must be supplied" unless field_name.to_s.start_with?('encrypted_')
81
+ raise "SymmetricEncryption for Mongoid. When encryption is enabled for a field it must either start with 'encrypted_' or the option :decrypt must be supplied" unless field_name.to_s.start_with?('encrypted_')
73
82
  decrypt_as = field_name.to_s['encrypted_'.length..-1]
74
83
  end
75
84
 
@@ -77,7 +86,7 @@ module Mongoid
77
86
  underlying_type = options[:type]
78
87
  options[:type] = String
79
88
 
80
- raise "Symmetric::Encryption for Mongoid currently only supports :type => String" unless underlying_type == String
89
+ raise "SymmetricEncryption for Mongoid currently only supports :type => String" unless underlying_type == String
81
90
 
82
91
  # #TODO Need to do type conversions. Currently only support String
83
92
 
@@ -86,7 +95,7 @@ module Mongoid
86
95
  # Set the un-encrypted bank account number
87
96
  # Also updates the encrypted field with the encrypted value
88
97
  def #{decrypt_as}=(value)
89
- @stored_#{field_name} = Symmetric::Encryption.encrypt(value)
98
+ @stored_#{field_name} = SymmetricEncryption.encrypt(value)
90
99
  self.#{field_name} = @stored_#{field_name}
91
100
  @#{decrypt_as} = value
92
101
  end
@@ -96,7 +105,7 @@ module Mongoid
96
105
  # If this method is not called, then the encrypted value is never decrypted
97
106
  def #{decrypt_as}
98
107
  if @stored_#{field_name} != self.#{field_name}
99
- @#{decrypt_as} = Symmetric::Encryption.decrypt(self.#{field_name})
108
+ @#{decrypt_as} = SymmetricEncryption.decrypt(self.#{field_name})
100
109
  @stored_#{field_name} = self.#{field_name}
101
110
  end
102
111
  @#{decrypt_as}
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- module Symmetric #:nodoc:
2
+ module SymmetricEncryption #:nodoc:
3
3
  class Railtie < Rails::Railtie #:nodoc:
4
4
 
5
5
  # Exposes Symmetric Encryption's configuration to the Rails application configuration.
@@ -10,7 +10,7 @@ module Symmetric #:nodoc:
10
10
  # config.symmetric_encryption.cipher = 'aes-256-cbc'
11
11
  # end
12
12
  # end
13
- #config.symmetric_encryption = ::Symmetric::Config
13
+ #config.symmetric_encryption = ::SymmetricEncryption::Config
14
14
 
15
15
  rake_tasks do
16
16
  load "symmetric/railties/symmetric_encryption.rake"
@@ -28,10 +28,10 @@ module Symmetric #:nodoc:
28
28
  #
29
29
  # Loaded before Active Record initializes since database.yml can have encrypted
30
30
  # passwords in it
31
- initializer "load symmetric encryption keys" , :before=>"active_record.initialize_database" do
31
+ initializer "symmetric-encryption.initialize" , :before=>"active_record.initialize_database" do
32
32
  config_file = Rails.root.join("config", "symmetric-encryption.yml")
33
33
  if config_file.file?
34
- ::Symmetric::Encryption.load!(config_file, Rails.env)
34
+ ::SymmetricEncryption.load!(config_file, Rails.env)
35
35
  else
36
36
  puts "\nSymmetric Encryption config not found. Create a config file at: config/symmetric-encryption.yml"
37
37
  # puts "to generate one run: rails generate symmetric-encryption:config\n\n"
@@ -3,7 +3,7 @@ namespace :symmetric_encryption do
3
3
  desc 'Decrypt the supplied string. Example: VALUE="_encrypted_string_" rake symmetric_encryption:decrypt'
4
4
  task :decrypt => :environment do
5
5
  puts "\nEncrypted: #{ENV['VALUE']}"
6
- puts "Decrypted: #{Symmetric::Encryption.decrypt(ENV['VALUE'])}\n\n"
6
+ puts "Decrypted: #{SymmetricEncryption.decrypt(ENV['VALUE'])}\n\n"
7
7
  end
8
8
 
9
9
  desc 'Encrypt a value, such as a password. Example: rake symmetric_encryption:encrypt'
@@ -20,19 +20,19 @@ namespace :symmetric_encryption do
20
20
  puts "Passwords do not match, please try again"
21
21
  end
22
22
  end
23
- puts "\nEncrypted: #{Symmetric::Encryption.encrypt(password1)}\n\n"
23
+ puts "\nEncrypted: #{SymmetricEncryption.encrypt(password1)}\n\n"
24
24
  end
25
25
 
26
26
  desc 'Generate new Symmetric key and initialization vector. Example: RAILS_ENV=production rake symmetric_encryption:generate_symmetric_keys'
27
27
  task :generate_symmetric_keys do
28
- Symmetric::Encryption.generate_symmetric_key_files
28
+ SymmetricEncryption.generate_symmetric_key_files
29
29
  end
30
30
 
31
31
  desc 'Generate a random password and display its encrypted form. Example: rake symmetric_encryption:random_password'
32
32
  task :random_password => :environment do
33
- p = Symmetric::Encryption.random_password
33
+ p = SymmetricEncryption.random_password
34
34
  puts "\nGenerated Password: #{p}"
35
- puts "Encrypted: #{Symmetric::Encryption.encrypt(p)}\n\n"
35
+ puts "Encrypted: #{SymmetricEncryption.encrypt(p)}\n\n"
36
36
  end
37
37
 
38
38
  end