shipstar-sentry 0.5.2.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.
data/lib/sentry.rb ADDED
@@ -0,0 +1,71 @@
1
+ #Copyright (c) 2005 Rick Olson
2
+ #
3
+ #Permission is hereby granted, free of charge, to any person obtaining
4
+ #a copy of this software and associated documentation files (the
5
+ #"Software"), to deal in the Software without restriction, including
6
+ #without limitation the rights to use, copy, modify, merge, publish,
7
+ #distribute, sublicense, and/or sell copies of the Software, and to
8
+ #permit persons to whom the Software is furnished to do so, subject to
9
+ #the following conditions:
10
+ #
11
+ #The above copyright notice and this permission notice shall be
12
+ #included in all copies or substantial portions of the Software.
13
+ #
14
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ #MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ #NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ #LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ #OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ #WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'openssl'
23
+ require 'base64'
24
+ require 'sentry/symmetric_sentry'
25
+ require 'sentry/asymmetric_sentry'
26
+ require 'sentry/sha_sentry'
27
+ require 'sentry/symmetric_sentry_callback'
28
+ require 'sentry/asymmetric_sentry_callback'
29
+
30
+ module Sentry
31
+ class NoKeyError < StandardError
32
+ end
33
+ class NoPublicKeyError < StandardError
34
+ end
35
+ class NoPrivateKeyError < StandardError
36
+ end
37
+ mattr_accessor :default_key
38
+ end
39
+
40
+ begin
41
+ require 'active_record/sentry'
42
+ ActiveRecord::Base.class_eval do
43
+ include ActiveRecord::Sentry
44
+ end
45
+ rescue NameError
46
+ nil
47
+ end
48
+
49
+ class OpenSSL::PKey::RSA
50
+ def max_encryptable_length
51
+ @max_encryption_length ||= calc_max_encrypted_length
52
+ end
53
+
54
+ private
55
+
56
+ def calc_max_encrypted_length
57
+ upper_bound = 4*1024
58
+ test_length = upper_bound / 2
59
+ while test_length != (upper_bound - 1)
60
+ probe = "a" * test_length
61
+ begin
62
+ self.public_encrypt(probe)
63
+ test_length = test_length + ((upper_bound - test_length) / 2)
64
+ rescue Exception => e
65
+ upper_bound = test_length
66
+ test_length = test_length / 2
67
+ end
68
+ end
69
+ return test_length
70
+ end
71
+ end
@@ -0,0 +1,190 @@
1
+ module Sentry
2
+ class AsymmetricSentry
3
+ attr_reader :private_key_file
4
+ attr_reader :public_key_file
5
+ attr_accessor :symmetric_algorithm
6
+ @@default_private_key_file = nil
7
+ @@default_public_key_file = nil
8
+ @@default_symmetric_algorithm = nil
9
+
10
+ # available options:
11
+ # * <tt>:private_key_file</tt> - encrypted private key file
12
+ # * <tt>:public_key_file</tt> - public key file
13
+ # * <tt>:symmetric_algorithm</tt> - algorithm to use for SymmetricSentry
14
+ def initialize(options = {})
15
+ @public_key = @private_key = nil
16
+ self.private_key_file = options[:private_key_file]
17
+ self.public_key_file = options[:public_key_file] || @@default_public_key_file
18
+ @symmetric_algorithm = options[:symmetric_algorithm] || @@default_symmetric_algorithm
19
+ end
20
+
21
+ def encrypt(data)
22
+ raise NoPublicKeyError unless public?
23
+ rsa = public_rsa
24
+ return rsa.public_encrypt(data)
25
+ end
26
+
27
+ def decrypt_large_from_base64(data, key=nil)
28
+ chunk_length = public_rsa.max_encryptable_length + 11 # 11 is magic padding for RSA encoding
29
+ b64_decoded = Base64.decode64(data)
30
+ padding_length = b64_decoded[0]
31
+ data = b64_decoded[1, data.length]
32
+ return (0...data.length).step(chunk_length).inject("") { |accum, idx| accum + decrypt_with_padding(data.slice(idx, chunk_length), padding_length, key)}
33
+ end
34
+
35
+ def chunk_size(padding_length)
36
+ return public_rsa.max_encryptable_length - padding_length
37
+ end
38
+
39
+ def encrypt_large_to_base64(data)
40
+ padding_length = 8
41
+ chunk_length = chunk_size(padding_length)
42
+ return Base64.encode64(padding_length.chr + (0...data.length).step(chunk_length).inject("") {|accum, idx| accum + encrypt_with_padding( data.slice(idx, chunk_length), padding_length)} )
43
+ end
44
+
45
+ def decrypt_with_padding(data, padding_length, key=nil)
46
+ decrypted = decrypt(data, key)
47
+ return decrypted[0, decrypted.length - padding_length]
48
+ end
49
+
50
+ def encrypt_with_padding(data, padding_length)
51
+ encrypt(data + rand_string(padding_length))
52
+ end
53
+
54
+ @@CHARS = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
55
+
56
+ def rand_string(length=8)
57
+ s=''
58
+ length.times{ s << @@CHARS[rand(@@CHARS.length)] }
59
+ s
60
+ end
61
+
62
+ def encrypt_to_base64(data)
63
+ Base64.encode64(encrypt(data))
64
+ end
65
+
66
+ def decrypt(data, key = nil)
67
+ raise NoPrivateKeyError unless private?
68
+ rsa = private_rsa(key)
69
+ return rsa.private_decrypt(data)
70
+ end
71
+
72
+ def decrypt_from_base64(data, key = nil)
73
+ decrypt(Base64.decode64(data), key)
74
+ end
75
+
76
+ def private_key_file=(file)
77
+ @private_key_file = file and load_private_key
78
+ end
79
+
80
+ def public_key_file=(file)
81
+ @public_key_file = file and load_public_key
82
+ end
83
+
84
+ def public?
85
+ return true unless @public_key.nil?
86
+ load_public_key and return @public_key
87
+ end
88
+
89
+ def private?
90
+ return true unless @private_key.nil?
91
+ load_private_key and return @private_key
92
+ end
93
+
94
+ class << self
95
+ # * <tt>:key</tt> - secret password
96
+ # * <tt>:symmetric_algorithm</tt> - symmetrical algorithm to use
97
+ def save_random_rsa_key(private_key_file, public_key_file, options = {})
98
+ rsa = OpenSSL::PKey::RSA.new(512)
99
+ public_key = rsa.public_key
100
+ private_key = options[:key].to_s.empty? ?
101
+ rsa.to_s :
102
+ SymmetricSentry.new(:algorithm => options[:symmetric_algorithm]).encrypt_to_base64(rsa.to_s, options[:key])
103
+ File.open(public_key_file, 'w') { |f| f.write(public_key) }
104
+ File.open(private_key_file, 'w') { |f| f.write(private_key) }
105
+ end
106
+
107
+ def encrypt(data)
108
+ self.new.encrypt(data)
109
+ end
110
+
111
+ def encrypt_to_base64(data)
112
+ self.new.encrypt_to_base64(data)
113
+ end
114
+
115
+ def encrypt_large_to_base64(data)
116
+ self.new.encrypt_large_to_base64(data)
117
+ end
118
+
119
+ def decrypt(data, key = nil)
120
+ self.new.decrypt(data, key)
121
+ end
122
+
123
+ def decrypt_large_from_base64(data, key = nil)
124
+ self.new.decrypt_large_from_base64(data, key)
125
+ end
126
+
127
+ def decrypt_from_base64(data, key = nil)
128
+ self.new.decrypt_from_base64(data, key)
129
+ end
130
+
131
+ # cattr_accessor would be lovely
132
+ def default_private_key_file
133
+ @@default_private_key_file
134
+ end
135
+
136
+ def default_private_key_file=(value)
137
+ @@default_private_key_file = value
138
+ end
139
+
140
+ def default_public_key_file
141
+ @@default_public_key_file
142
+ end
143
+
144
+ def default_public_key_file=(value)
145
+ @@default_public_key_file = value
146
+ end
147
+
148
+ def default_symmetric_algorithm
149
+ @@default_symmetric_algorithm
150
+ end
151
+
152
+ def default_symmetric_algorithm=(value)
153
+ @@default_symmetric_algorithm = value
154
+ end
155
+ end
156
+
157
+ private
158
+ def encryptor
159
+ @encryptor ||= SymmetricSentry.new(:algorithm => @symmetric_algorithm)
160
+ end
161
+
162
+ def load_private_key
163
+ @private_rsa = nil
164
+ @private_key_file ||= @@default_private_key_file
165
+ if @private_key_file and File.file?(@private_key_file)
166
+ @private_key = File.open(@private_key_file) { |f| f.read }
167
+ end
168
+ return @private_key
169
+ end
170
+
171
+ def load_public_key
172
+ @public_rsa = nil
173
+ @public_key_file ||= @@default_public_key_file
174
+ if @public_key_file and File.file?(@public_key_file)
175
+ @public_key = File.open(@public_key_file) { |f| f.read }
176
+ end
177
+ end
178
+
179
+ # retrieves private rsa from encrypted private key
180
+ def private_rsa(key = nil)
181
+ return @private_rsa ||= OpenSSL::PKey::RSA.new(@private_key) unless key
182
+ OpenSSL::PKey::RSA.new(encryptor.decrypt_from_base64(@private_key, key))
183
+ end
184
+
185
+ # retrieves public rsa
186
+ def public_rsa
187
+ @public_rsa ||= OpenSSL::PKey::RSA.new(@public_key)
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,17 @@
1
+ module Sentry
2
+ class AsymmetricSentryCallback
3
+ def initialize(attr_name)
4
+ @attr_name = attr_name
5
+ end
6
+
7
+ # Performs encryption on before_validation Active Record callback
8
+ #def before_validation(model)
9
+ # return if model.send(@attr_name).blank?
10
+ # model.send("crypted_#{@attr_name}=", AsymmetricSentry.encrypt_to_base64(model.send(@attr_name)))
11
+ #end
12
+
13
+ #def after_save(model)
14
+ # model.send("#{@attr_name}=", nil)
15
+ #end
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ require 'digest/sha1'
2
+ module Sentry
3
+ class ShaSentry
4
+ @@salt = 'salt'
5
+ attr_accessor :salt
6
+
7
+ # Encrypts data using SHA.
8
+ def encrypt(data)
9
+ self.class.encrypt(data + salt.to_s)
10
+ end
11
+
12
+ # Initialize the class.
13
+ # Used by ActiveRecord::Base#generates_crypted to set up as a callback object for a model
14
+ def initialize(attribute = nil)
15
+ @attribute = attribute
16
+ end
17
+
18
+ # Performs encryption on before_validation Active Record callback
19
+ def before_validation(model)
20
+ return unless model.send(@attribute)
21
+ model.send("crypted_#{@attribute}=", encrypt(model.send(@attribute)))
22
+ end
23
+
24
+ class << self
25
+ # Gets the class salt value used when encrypting
26
+ def salt
27
+ @@salt
28
+ end
29
+
30
+ # Sets the class salt value used when encrypting
31
+ def salt=(value)
32
+ @@salt = value
33
+ end
34
+
35
+ # Encrypts the data
36
+ def encrypt(data)
37
+ Digest::SHA1.hexdigest(data + @@salt)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,77 @@
1
+ module Sentry
2
+ class SymmetricSentry
3
+ @@default_algorithm = 'DES-EDE3-CBC'
4
+ @@default_key = nil
5
+ attr_accessor :algorithm
6
+ def initialize(options = {})
7
+ @algorithm = options[:algorithm] || @@default_algorithm
8
+ end
9
+
10
+ def encrypt(data, key = nil)
11
+ key = check_for_key!(key)
12
+ encryptor.pkcs5_keyivgen(key)
13
+ encryptor.encrypt
14
+ encryptor.update(data) + encryptor.final
15
+ end
16
+
17
+ def encrypt_to_base64(text, key = nil)
18
+ Base64.encode64(encrypt(text, key))
19
+ end
20
+
21
+ def decrypt(data, key = nil)
22
+ key = check_for_key!(key)
23
+ encryptor.pkcs5_keyivgen(key)
24
+ encryptor.decrypt
25
+ encryptor.update(data) + encryptor.final
26
+ end
27
+
28
+ def decrypt_from_base64(text, key = nil)
29
+ decrypt(Base64.decode64(text), key)
30
+ end
31
+
32
+ class << self
33
+ def default_algorithm
34
+ @@default_algorithm
35
+ end
36
+
37
+ def default_algorithm=(value)
38
+ @@default_algorithm = value
39
+ end
40
+
41
+ def default_key
42
+ @@default_key
43
+ end
44
+
45
+ def default_key=(value)
46
+ @@default_key = value
47
+ end
48
+
49
+ def encrypt(data, key = nil)
50
+ self.new.encrypt(data, key)
51
+ end
52
+
53
+ def encrypt_to_base64(text, key = nil)
54
+ self.new.encrypt_to_base64(text, key)
55
+ end
56
+
57
+ def decrypt(data, key = nil)
58
+ self.new.decrypt(data, key)
59
+ end
60
+
61
+ def decrypt_from_base64(text, key = nil)
62
+ self.new.decrypt_from_base64(text, key)
63
+ end
64
+ end
65
+
66
+ private
67
+ def encryptor
68
+ @encryptor ||= OpenSSL::Cipher::Cipher.new(@algorithm)
69
+ end
70
+
71
+ def check_for_key!(key)
72
+ valid_key = key || @@default_key
73
+ raise Sentry::NoKeyError if valid_key.nil?
74
+ valid_key
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,17 @@
1
+ module Sentry
2
+ class SymmetricSentryCallback
3
+ def initialize(attr_name)
4
+ @attr_name = attr_name
5
+ end
6
+
7
+ ## Performs encryption on before_validation Active Record callback
8
+ #def before_validation(model)
9
+ # return if model.send(@attr_name).blank?
10
+ # model.send("crypted_#{@attr_name}=", SymmetricSentry.encrypt_to_base64(model.send(@attr_name)))
11
+ #end
12
+
13
+ #def after_save(model)
14
+ # #model.send("#{@attr_name}=", nil)
15
+ #end
16
+ end
17
+ end
data/sentry.gemspec ADDED
@@ -0,0 +1,79 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{sentry}
8
+ s.version = "0.5.2.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Kyle Shipley"]
12
+ s.date = %q{2009-08-19}
13
+ s.description = %q{Asymmetric encryption of active record fields}
14
+ s.email = %q{shipstar@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README"
17
+ ]
18
+ s.files = [
19
+ "CHANGELOG",
20
+ "MIT-LICENSE",
21
+ "README",
22
+ "RUNNING_UNIT_TESTS",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "init.rb",
26
+ "lib/active_record/sentry.rb",
27
+ "lib/sentry.rb",
28
+ "lib/sentry/asymmetric_sentry.rb",
29
+ "lib/sentry/asymmetric_sentry_callback.rb",
30
+ "lib/sentry/sha_sentry.rb",
31
+ "lib/sentry/symmetric_sentry.rb",
32
+ "lib/sentry/symmetric_sentry_callback.rb",
33
+ "sentry.gemspec",
34
+ "tasks/sentry.rake",
35
+ "test/abstract_unit.rb",
36
+ "test/asymmetric_sentry_callback_test.rb",
37
+ "test/asymmetric_sentry_test.rb",
38
+ "test/database.yml",
39
+ "test/fixtures/user.rb",
40
+ "test/fixtures/users.yml",
41
+ "test/keys/encrypted_private",
42
+ "test/keys/encrypted_public",
43
+ "test/keys/private",
44
+ "test/keys/public",
45
+ "test/rsa_key_test.rb",
46
+ "test/schema.rb",
47
+ "test/sha_sentry_test.rb",
48
+ "test/symmetric_sentry_callback_test.rb",
49
+ "test/symmetric_sentry_test.rb",
50
+ "test/tests.rb"
51
+ ]
52
+ s.homepage = %q{http://github.com/shipstar/sentry}
53
+ s.rdoc_options = ["--charset=UTF-8"]
54
+ s.require_paths = ["lib"]
55
+ s.rubygems_version = %q{1.3.5}
56
+ s.summary = %q{Asymmetric encryption of active record fields}
57
+ s.test_files = [
58
+ "test/abstract_unit.rb",
59
+ "test/asymmetric_sentry_callback_test.rb",
60
+ "test/asymmetric_sentry_test.rb",
61
+ "test/fixtures/user.rb",
62
+ "test/rsa_key_test.rb",
63
+ "test/schema.rb",
64
+ "test/sha_sentry_test.rb",
65
+ "test/symmetric_sentry_callback_test.rb",
66
+ "test/symmetric_sentry_test.rb",
67
+ "test/tests.rb"
68
+ ]
69
+
70
+ if s.respond_to? :specification_version then
71
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
72
+ s.specification_version = 3
73
+
74
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
75
+ else
76
+ end
77
+ else
78
+ end
79
+ end