encrypted_store 0.2.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.
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: []