encrypted_store 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 597b51a7a639daa48f0168cd455614d0badc530d
4
+ data.tar.gz: 0c3ac74f182c8825f0bf8a2cb40b5c6c1636e221
5
+ SHA512:
6
+ metadata.gz: f7abe54066eb6000e60e38ac7e6bf8df46f3b5e50d6d85957b38c56436dde3221e18730a4d21a7076729e96b673f887325856b419cda5be666b02c506168db56
7
+ data.tar.gz: 8a280ee382d0de6e35f34bbe3b9ec683f57583bddb6a3cd823f0beb10c52e4348f3724f25344b220cb2d836172a0fb8b5c353b80944b24ab12f95a44df69d858
@@ -0,0 +1,12 @@
1
+ module Ribbon::EncryptedStore
2
+ module Generators
3
+ class EncryptTableGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+ argument :table_name, :type => :string
6
+
7
+ def create_migrations
8
+ generate "migration", "add_encrypted_store_to_#{table_name} encryption_key_id:integer encrypted_store:binary"
9
+ end
10
+ end # EncryptTableGenerator
11
+ end # Generators
12
+ end # Ribbon::EncryptedStore
@@ -0,0 +1,26 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module Ribbon::EncryptedStore
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+
8
+ class << self
9
+ def next_migration_number(*args)
10
+ ActiveRecord::Generators::Base.next_migration_number(*args)
11
+ end
12
+ end # Class Methods
13
+
14
+ source_root File.expand_path("../templates", __FILE__)
15
+
16
+ def create_initializer
17
+ copy_file "initializer.rb", "config/initializers/encrypted_store.rb"
18
+ end
19
+
20
+ def create_migrations
21
+ migration_template 'create_encryption_keys.rb', 'db/migrate/create_encryption_keys.rb'
22
+ migration_template 'create_encryption_key_salts.rb', 'db/migrate/create_encryption_key_salts.rb'
23
+ end
24
+ end # InstallEncryptedStoreGenerator
25
+ end # Generators
26
+ end # Ribbon::EncryptedStore
@@ -0,0 +1,12 @@
1
+ class CreateEncryptionKeySalts < ActiveRecord::Migration
2
+ def change
3
+ create_table :encryption_key_salts do |t|
4
+ t.integer :encryption_key_id
5
+ t.binary :salt
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :encryption_key_salts, [:encryption_key_id, :salt], unique: true
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ class CreateEncryptionKeys < ActiveRecord::Migration
2
+ def change
3
+ create_table :encryption_keys do |t|
4
+ t.binary :dek
5
+ t.boolean :primary
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :encryption_keys, :created_at
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ require 'ribbon/encrypted_store'
2
+
3
+ Ribbon::EncryptedStore.config {
4
+ encrypt_key { |dek| dek }
5
+ decrypt_key { |dek| dek }
6
+ }
@@ -0,0 +1,8 @@
1
+ class UpgradeEncryptionKeySaltsTo015 < ActiveRecord::Migration
2
+ def change
3
+ add_column :encryption_key_salts, :created_at, :datetime
4
+ add_column :encryption_key_salts, :updated_at, :datetime
5
+
6
+ add_index :encryption_key_salts, [:encryption_key_id, :salt], unique: true
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ class UpgradeEncryptionKeysTo015 < ActiveRecord::Migration
2
+ def change
3
+ add_column :encryption_keys, :created_at, :datetime
4
+ add_column :encryption_keys, :updated_at, :datetime
5
+
6
+ add_index :encryption_keys, :created_at
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module Ribbon::EncryptedStore
4
+ module Generators
5
+ module Upgrade
6
+ class ZeroOneFiveGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ class << self
10
+ def next_migration_number(*args)
11
+ ActiveRecord::Generators::Base.next_migration_number(*args)
12
+ end
13
+ end # Class Methods
14
+
15
+ source_root File.expand_path("../templates", __FILE__)
16
+
17
+ def create_migrations
18
+ migration_template 'upgrade_encryption_keys_to_015.rb', 'db/migrate/upgrade_encryption_keys_to_015.rb'
19
+ migration_template 'upgrade_encryption_key_salts_to_015.rb', 'db/migrate/upgrade_encryption_key_salts_to_015.rb'
20
+ end
21
+ end # ZeroOneFiveGenerator
22
+ end # Upgrade
23
+ end # Generators
24
+ end # Ribbon::EncryptedStore
@@ -0,0 +1,33 @@
1
+ require 'ribbon/encrypted_store/version'
2
+ require 'ribbon/config'
3
+
4
+ module Ribbon
5
+ module EncryptedStore
6
+ require 'ribbon/encrypted_store/railtie' if defined?(Rails)
7
+ autoload(:CryptoHash, 'ribbon/encrypted_store/crypto_hash')
8
+ autoload(:Instance, 'ribbon/encrypted_store/instance')
9
+ autoload(:Errors, 'ribbon/encrypted_store/errors')
10
+ autoload(:Mixins, 'ribbon/encrypted_store/mixins')
11
+
12
+ class << self
13
+ def included(base)
14
+ if defined?(ActiveRecord) && base < ActiveRecord::Base
15
+ base.send(:include, Mixins::ActiveRecordMixin)
16
+ else
17
+ raise Errors::UnsupportedModelError
18
+ end
19
+ end
20
+
21
+ def method_missing(meth, *args, &block)
22
+ instance.send(meth, *args, &block)
23
+ end
24
+
25
+ def instance
26
+ @__instance ||= Instance.new
27
+ end
28
+ end # Class Methods
29
+ end # EncryptedStore
30
+ end # Ribbon
31
+
32
+ # Create a shortcut to the module
33
+ EncryptedStore = Ribbon::EncryptedStore
@@ -0,0 +1,150 @@
1
+ require 'openssl'
2
+ require 'json'
3
+ require 'zlib'
4
+
5
+ module Ribbon::EncryptedStore
6
+ class CryptoHash < Hash
7
+ def initialize(data={})
8
+ super()
9
+ merge!(data)
10
+ end
11
+
12
+ ##
13
+ # Encrypts the hash using the data encryption key and salt.
14
+ #
15
+ # Returns a blob:
16
+ # | Byte 0 | Byte 1 | Byte 2 | Bytes 3...S | Bytes S+1...E | Bytes E+1..E+4 |
17
+ # ------------------------------------------------------------------------------------------------
18
+ # | Version | Salt Length | Iteration Magnitude | Salt | Encrypted Data | CRC32 |
19
+ def encrypt(dek, salt, iter_mag=10)
20
+ return nil if empty?
21
+ raise Errors::InvalidSaltSize, 'too long' if salt.bytes.length > 255
22
+
23
+ key, iv = _keyiv_gen(dek, salt, iter_mag)
24
+
25
+ encryptor = OpenSSL::Cipher::AES256.new(:CBC).encrypt
26
+ encryptor.key = key
27
+ encryptor.iv = iv
28
+
29
+ data_packet = _encrypted_data_header_v2(salt, iter_mag) + encryptor.update(self.to_json) + encryptor.final
30
+ _append_crc32(data_packet)
31
+ end
32
+
33
+ class << self
34
+ def decrypt(dek, data)
35
+ return CryptoHash.new unless data
36
+ salt, iter_mag, data = _split_binary_data(data)
37
+
38
+ key, iv = _keyiv_gen(dek, salt, iter_mag)
39
+
40
+ decryptor = OpenSSL::Cipher::AES256.new(:CBC).decrypt
41
+ decryptor.key = key
42
+ decryptor.iv = iv
43
+
44
+ new_hash = JSON.parse(decryptor.update(data) + decryptor.final)
45
+ new_hash = Hash[new_hash.map { |k,v| [k.to_sym, v] }]
46
+ CryptoHash.new(new_hash)
47
+ end
48
+
49
+ def _keyiv_gen(key, salt, iter_mag)
50
+ if iter_mag == -1
51
+ raise Errors::InvalidKeySize, 'must be exactly 256 bits' unless key.bytes.length == 32
52
+ raise Errors::InvalidSaltSize, 'must be exactly 128 bits' unless salt.bytes.length == 16
53
+ iv = salt
54
+ else
55
+ digest = OpenSSL::Digest::SHA256.new
56
+ key_and_iv = OpenSSL::PKCS5.pbkdf2_hmac(key, salt, 1 << iter_mag, 48, digest)
57
+
58
+ key = key_and_iv[0..31]
59
+ iv = key_and_iv[32..-1]
60
+ end
61
+
62
+ [key, iv]
63
+ end
64
+
65
+ def _split_binary_data(encrypted_data)
66
+ # Split encrypted data and CRC
67
+ bytes = encrypted_data.bytes
68
+
69
+ version = bytes[0]
70
+ version_method = "_split_binary_data_v#{version}"
71
+
72
+ if respond_to?(version_method)
73
+ send(version_method, encrypted_data)
74
+ else
75
+ raise Errors::UnsupportedVersionError, "Unsupported encrypted data version: #{version}"
76
+ end
77
+ end
78
+
79
+ def _split_binary_data_v1(encrypted_data)
80
+ bytes = encrypted_data.bytes
81
+ salt_length = bytes[1]
82
+
83
+ salt_start_index = 2
84
+ salt_end_index = salt_start_index + salt_length - 1
85
+ salt = bytes[salt_start_index..salt_end_index].pack('c*')
86
+ data = bytes[salt_end_index+1..-5].pack('c*')
87
+
88
+ crc = bytes[-4..-1]
89
+ raise Errors::ChecksumFailedError unless crc == _calc_crc32(encrypted_data[0..-5]).bytes
90
+
91
+ [salt, 12, data]
92
+ end
93
+
94
+ def _split_binary_data_v2(encrypted_data)
95
+ bytes = encrypted_data.bytes
96
+ salt_length = bytes[1]
97
+ iter_mag = bytes[2].chr.unpack('c').first
98
+
99
+ salt_start_index = 3
100
+ salt_end_index = salt_start_index + salt_length - 1
101
+ salt = bytes[salt_start_index..salt_end_index].pack('c*')
102
+ data = bytes[salt_end_index+1..-5].pack('c*')
103
+
104
+ crc = bytes[-4..-1]
105
+ raise Errors::ChecksumFailedError unless crc == _calc_crc32(encrypted_data[0..-5]).bytes
106
+
107
+ [salt, iter_mag, data]
108
+ end
109
+
110
+
111
+ def _calc_crc32(data)
112
+ [Zlib.crc32(data)].pack('N')
113
+ end
114
+ end # Class Methods
115
+
116
+ private
117
+
118
+ ##
119
+ # Generates the version 1 encrypted data header:
120
+ # | Byte 0 | Byte 1 | Bytes 2...S
121
+ # ---------------------------------------------------
122
+ # | Version | Salt Length | Salt
123
+ #
124
+ def _encrypted_data_header_v1(salt)
125
+ "\x01" + salt.bytes.length.chr + salt
126
+ end
127
+
128
+ ##
129
+ # Generates the version 2 encrypted data header:
130
+ # | Byte 0 | Byte 1 | Byte 2 | Bytes 3...S
131
+ # ----------------------------------------------------------------------
132
+ # | Version | Salt Length | Iteration Magnitude | Salt
133
+ #
134
+ def _encrypted_data_header_v2(salt, iter_mag)
135
+ "\x02" + salt.bytes.length.chr + [iter_mag].pack('c') + salt
136
+ end
137
+
138
+ def _keyiv_gen(key, salt, iter_mag)
139
+ self.class._keyiv_gen(key, salt, iter_mag)
140
+ end
141
+
142
+ def _append_crc32(data)
143
+ data + _calc_crc32(data)
144
+ end
145
+
146
+ def _calc_crc32(data)
147
+ self.class._calc_crc32(data)
148
+ end
149
+ end # CryptoHash
150
+ end # Ribbon::EncryptedStore
@@ -0,0 +1,16 @@
1
+ module Ribbon::EncryptedStore
2
+ module Errors
3
+ class Error < StandardError; end
4
+
5
+ # General Errors
6
+ class GeneralError < Error; end
7
+ class UnsupportedModelError < GeneralError; end
8
+
9
+ # CryptoHash Errors
10
+ class CryptoHashError < Error; end
11
+ class ChecksumFailedError < CryptoHashError; end
12
+ class InvalidSaltSize < CryptoHashError; end
13
+ class InvalidKeySize < CryptoHashError; end
14
+ class UnsupportedVersionError < CryptoHashError; end
15
+ end # Errors
16
+ end # Ribbon::EncryptedStore
@@ -0,0 +1,30 @@
1
+ module Ribbon::EncryptedStore
2
+ class Instance
3
+ def config(&block)
4
+ (@__config ||= Ribbon::Config.new).tap { |config|
5
+ if block_given?
6
+ config.define(&block)
7
+ end
8
+ }
9
+ end
10
+
11
+ ##
12
+ # Preloads the most recent `amount` keys.
13
+ def preload_keys(amount=12)
14
+ keys = Mixins::ActiveRecordMixin.preload_keys(amount)
15
+ keys.each { |k| (@_decrypted_keys ||= {})[k.id] = k.decrypted_key }
16
+ end
17
+
18
+ def decrypt_key(dek, primary=false)
19
+ config.decrypt_key? ? config.decrypt_key.last.call(dek, primary) : dek
20
+ end
21
+
22
+ def encrypt_key(dek, primary=false)
23
+ config.encrypt_key? ? config.encrypt_key.last.call(dek, primary) : dek
24
+ end
25
+
26
+ def retrieve_dek(key_model, key_id)
27
+ (@_decrypted_keys ||= {})[key_id] ||= key_model.find(key_id).decrypted_key
28
+ end
29
+ end # Instance
30
+ end # Ribbon::EncryptedStore
@@ -0,0 +1,5 @@
1
+ module Ribbon::EncryptedStore
2
+ module Mixins
3
+ autoload(:ActiveRecordMixin, 'ribbon/encrypted_store/mixins/active_record_mixin')
4
+ end # Mixins
5
+ end # Ribbon::EncryptedStore
@@ -0,0 +1,108 @@
1
+ require 'securerandom'
2
+
3
+ module Ribbon::EncryptedStore
4
+ module Mixins
5
+ module ActiveRecordMixin
6
+ autoload(:EncryptionKeySalt, 'ribbon/encrypted_store/mixins/active_record_mixin/encryption_key_salt')
7
+ autoload(:EncryptionKey, 'ribbon/encrypted_store/mixins/active_record_mixin/encryption_key')
8
+
9
+ class << self
10
+ def included(base)
11
+ base.before_save(:_encrypted_store_save)
12
+ base.extend(ClassMethods)
13
+ end
14
+
15
+ def descendants
16
+ Rails.application.eager_load! if defined?(Rails) && Rails.application
17
+ ActiveRecord::Base.descendants.select { |model| model < Mixins::ActiveRecordMixin }
18
+ end
19
+
20
+ def descendants?
21
+ !descendants.empty?
22
+ end
23
+
24
+ ##
25
+ # Preloads the most recent `amount` keys.
26
+ def preload_keys(amount)
27
+ EncryptionKey.preload(amount) if descendants?
28
+ end
29
+ end # Module Methods
30
+
31
+ module ClassMethods
32
+ def _encrypted_store_data
33
+ @_encrypted_store_data ||= {}
34
+ end
35
+
36
+ def attr_encrypted(*args)
37
+ # Store attrs in class data
38
+ _encrypted_store_data[:encrypted_attributes] = args.map(&:to_sym)
39
+
40
+ args.each { |arg|
41
+ define_method(arg) { _encrypted_store_get(arg) }
42
+ define_method("#{arg}=") { |value| _encrypted_store_set(arg, value) }
43
+ }
44
+ end
45
+ end # ClassMethods
46
+
47
+ ##
48
+ # Instance Methods
49
+ ##
50
+ def reencrypt(encryption_key)
51
+ _crypto_hash
52
+ self.encryption_key_id = encryption_key.id
53
+ @_reencrypting = true
54
+ end
55
+
56
+ def reencrypt!(encryption_key)
57
+ reencrypt(encryption_key).tap { save! }
58
+ end
59
+
60
+ def _encrypted_store_data
61
+ self.class._encrypted_store_data
62
+ end
63
+
64
+ def _encryption_key_id
65
+ self.encryption_key_id ||= EncryptionKey.primary_encryption_key.id
66
+ end
67
+
68
+ def _crypto_hash
69
+ @_crypto_hash ||= CryptoHash.decrypt(_decrypted_key, self.encrypted_store)
70
+ end
71
+
72
+ def _decrypted_key
73
+ EncryptedStore.retrieve_dek(EncryptionKey, _encryption_key_id)
74
+ end
75
+
76
+ def _encrypted_store_get(field)
77
+ _crypto_hash[field]
78
+ end
79
+
80
+ def _encrypted_store_set(field, value)
81
+ attribute_will_change!(field)
82
+ _crypto_hash[field] = value
83
+ end
84
+
85
+ def _encrypted_store_save
86
+ if !(self.changed.map(&:to_sym) & _encrypted_store_data[:encrypted_attributes]).empty? || @_reencrypting
87
+ # Obtain a lock without overriding attribute values for this record.
88
+ record = self.class.unscoped { self.class.lock.find(id) } unless new_record?
89
+
90
+ unless @_reencrypting
91
+ self.encryption_key_id = record.encryption_key_id if record && record.encryption_key_id
92
+ end
93
+
94
+ iter_mag = Ribbon::EncryptedStore.config.iteration_magnitude? ?
95
+ Ribbon::EncryptedStore.config.iteration_magnitude :
96
+ -1
97
+
98
+ @_reencrypting = false
99
+ self.encrypted_store = _crypto_hash.encrypt(
100
+ _decrypted_key,
101
+ EncryptionKeySalt.generate_salt(_encryption_key_id),
102
+ iter_mag
103
+ )
104
+ end
105
+ end
106
+ end # ActiveRecordMixin
107
+ end # Mixins
108
+ end # Ribbon::EncryptedStore
@@ -0,0 +1,77 @@
1
+ require 'securerandom'
2
+ require 'base64'
3
+
4
+ module Ribbon::EncryptedStore
5
+ module Mixins
6
+ module ActiveRecordMixin
7
+ class EncryptionKey < ActiveRecord::Base
8
+ validates_uniqueness_of :primary, if: :primary
9
+
10
+ class << self
11
+ def primary_encryption_key
12
+ new_key unless _has_primary?
13
+ where(primary: true).last || last
14
+ end
15
+
16
+ def new_key(custom_key=nil)
17
+ dek = custom_key || SecureRandom.random_bytes(32)
18
+
19
+ transaction {
20
+ _has_primary? && where(primary: true).first.update_attributes(primary: false)
21
+ _create_primary_key(dek)
22
+ }
23
+ end
24
+
25
+ def retire_keys(key_ids=[])
26
+ pkey = primary_encryption_key
27
+
28
+ ActiveRecordMixin.descendants.each { |model|
29
+ records = key_ids.empty? ? model.where("encryption_key_id != ?", pkey.id)
30
+ : model.where("encryption_key_id IN (?)", key_ids)
31
+ records.each { |record| record.reencrypt!(pkey) }
32
+ }
33
+
34
+ pkey
35
+ end
36
+
37
+ ##
38
+ # Preload the most recent `amount` keys.
39
+ def preload(amount)
40
+ primary_encryption_key # Ensure there's at least a primary key
41
+ order(:created_at).limit(amount)
42
+ end
43
+
44
+ def rotate_keys
45
+ new_key
46
+ retire_keys
47
+ end
48
+
49
+ def _has_primary?
50
+ where(primary: true).exists?
51
+ end
52
+
53
+ def _get_table_models
54
+ Rails.application.eager_load! if defined?(Rails) && Rails.application
55
+ ActiveRecord::Base.descendants
56
+ end
57
+
58
+ def _get_models_with_encrypted_store
59
+ _get_table_models.select { |model| model < Mixins::ActiveRecordMixin }
60
+ end
61
+
62
+ def _create_primary_key(dek)
63
+ self.new.tap { |key|
64
+ key.dek = EncryptedStore.encrypt_key(dek, true)
65
+ key.primary = true
66
+ key.save!
67
+ }
68
+ end
69
+ end # Class Methods
70
+
71
+ def decrypted_key
72
+ EncryptedStore.decrypt_key(self.dek, self.primary)
73
+ end
74
+ end # EncryptionKey
75
+ end # ActiveRecordMixin
76
+ end # Mixins
77
+ end # Ribbon::EncryptedStore
@@ -0,0 +1,28 @@
1
+ require 'securerandom'
2
+
3
+ module Ribbon::EncryptedStore
4
+ module Mixins
5
+ module ActiveRecordMixin
6
+ class EncryptionKeySalt < ActiveRecord::Base
7
+ validates :salt, uniqueness: {scope: :encryption_key_id}
8
+
9
+ class << self
10
+ def generate_salt(encryption_key_id)
11
+ loop do
12
+ salt = SecureRandom.random_bytes(16)
13
+ begin
14
+ salt_record = self.new
15
+ salt_record.encryption_key_id = encryption_key_id
16
+ salt_record.salt = salt
17
+ salt_record.save!
18
+ return salt
19
+ rescue ActiveRecord::RecordNotUnique => e
20
+ next
21
+ end
22
+ end
23
+ end
24
+ end # Class Methods
25
+ end # EncryptionKeySalt
26
+ end # ActiveRecordMixin
27
+ end # Mixins
28
+ end # Ribbon::EncryptedStore
@@ -0,0 +1,23 @@
1
+ require 'ribbon/encrypted_store'
2
+ require 'rails'
3
+ require 'rails/generators'
4
+
5
+ module Ribbon
6
+ module EncryptedStore
7
+ class Railtie < Rails::Railtie
8
+ railtie_name :encrypted_store
9
+
10
+ rake_tasks do
11
+ Dir[
12
+ File.expand_path("../../../tasks", __FILE__) + '/**/*.rake'
13
+ ].each { |rake_file| load rake_file }
14
+ end
15
+
16
+ generators do
17
+ Dir[
18
+ File.expand_path("../../../generators", __FILE__) + '/**/*.rb'
19
+ ].each { |generator| require generator }
20
+ end
21
+ end # Railtie
22
+ end # EncryptedStore
23
+ end # Ribbon
@@ -0,0 +1,5 @@
1
+ module Ribbon
2
+ module EncryptedStore
3
+ VERSION = '0.2.0'
4
+ end # EncryptedStore
5
+ end # Ribbon
@@ -0,0 +1,19 @@
1
+ require 'ribbon/encrypted_store'
2
+
3
+ namespace :encrypted_store do
4
+ task :new_key, [:custom_key] => :environment do |t, args|
5
+ new_key = EncryptedStore::Mixins::ActiveRecordMixin::EncryptionKey.new_key(args[:custom_key])
6
+ puts "Created new primary key: #{new_key.id}"
7
+ end
8
+
9
+ task :retire_keys, [:key_ids] => :environment do |t, args|
10
+ key_ids = (args[:key_ids] && args[:key_ids].split(" ")) || []
11
+ new_primary_key = EncryptedStore::Mixins::ActiveRecordMixin::EncryptionKey.retire_keys(key_ids)
12
+ puts "Retired key_ids: #{key_ids} and reencrypted records with primary key: #{new_primary_key.id}"
13
+ end
14
+
15
+ task :rotate_keys => :environment do |t, args|
16
+ new_primary_key = EncryptedStore::Mixins::ActiveRecordMixin::EncryptionKey.rotate_keys
17
+ puts "Retired all key_ids and reencrypted records with new primary key: #{new_primary_key.id}"
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: encrypted_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Robert Honer
8
+ - Kayvon Ghaffari
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-10-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bcrypt
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 3.1.3
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 3.1.3
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: 3.1.3
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.1.3
34
+ - !ruby/object:Gem::Dependency
35
+ name: ribbon-config
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.0
41
+ type: :runtime
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.1.0
48
+ - !ruby/object:Gem::Dependency
49
+ name: database_cleaner
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ - !ruby/object:Gem::Dependency
77
+ name: rspec-rails
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: pg
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ - !ruby/object:Gem::Dependency
105
+ name: rails
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 4.0.0
111
+ type: :development
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 4.0.0
118
+ description: Provides the EncryptedStore mixin
119
+ email:
120
+ - robert@ribbonpayments.com
121
+ - kayvon@ribbonpayments.com
122
+ executables: []
123
+ extensions: []
124
+ extra_rdoc_files: []
125
+ files:
126
+ - lib/generators/encrypted_store/encrypt_table/encrypt_table_generator.rb
127
+ - lib/generators/encrypted_store/install/install_generator.rb
128
+ - lib/generators/encrypted_store/install/templates/create_encryption_key_salts.rb
129
+ - lib/generators/encrypted_store/install/templates/create_encryption_keys.rb
130
+ - lib/generators/encrypted_store/install/templates/initializer.rb
131
+ - lib/generators/encrypted_store/upgrade/ZeroOneFive/templates/upgrade_encryption_key_salts_to_015.rb
132
+ - lib/generators/encrypted_store/upgrade/ZeroOneFive/templates/upgrade_encryption_keys_to_015.rb
133
+ - lib/generators/encrypted_store/upgrade/ZeroOneFive/zero_one_five_generator.rb
134
+ - lib/ribbon/encrypted_store.rb
135
+ - lib/ribbon/encrypted_store/crypto_hash.rb
136
+ - lib/ribbon/encrypted_store/errors.rb
137
+ - lib/ribbon/encrypted_store/instance.rb
138
+ - lib/ribbon/encrypted_store/mixins.rb
139
+ - lib/ribbon/encrypted_store/mixins/active_record_mixin.rb
140
+ - lib/ribbon/encrypted_store/mixins/active_record_mixin/encryption_key.rb
141
+ - lib/ribbon/encrypted_store/mixins/active_record_mixin/encryption_key_salt.rb
142
+ - lib/ribbon/encrypted_store/railtie.rb
143
+ - lib/ribbon/encrypted_store/version.rb
144
+ - lib/tasks/encrypted_store.rake
145
+ homepage: http://github.com/ribbon/encrypted_store
146
+ licenses:
147
+ - BSD
148
+ metadata: {}
149
+ post_install_message:
150
+ rdoc_options: []
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ requirements: []
164
+ rubyforge_project:
165
+ rubygems_version: 2.4.3
166
+ signing_key:
167
+ specification_version: 4
168
+ summary: Provides the EncryptedStore mixin
169
+ test_files: []