symmetric-encryption 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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