stderr-sentry 0.5.4
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/.gemspec +78 -0
- data/.gitignore +3 -0
- data/CHANGELOG +58 -0
- data/MIT-LICENSE +20 -0
- data/README +94 -0
- data/RUNNING_UNIT_TESTS +42 -0
- data/Rakefile +197 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/active_record/sentry.rb +105 -0
- data/lib/sentry.rb +71 -0
- data/lib/sentry/asymmetric_sentry.rb +192 -0
- data/lib/sentry/asymmetric_sentry_callback.rb +17 -0
- data/lib/sentry/sha_sentry.rb +41 -0
- data/lib/sentry/symmetric_sentry.rb +82 -0
- data/lib/sentry/symmetric_sentry_callback.rb +17 -0
- data/sentry.gemspec +82 -0
- data/tasks/sentry.rake +9 -0
- data/test/abstract_unit.rb +44 -0
- data/test/asymmetric_sentry_callback_test.rb +122 -0
- data/test/asymmetric_sentry_test.rb +97 -0
- data/test/database.yml +18 -0
- data/test/fixtures/user.rb +26 -0
- data/test/fixtures/users.yml +9 -0
- data/test/keys/encrypted_private +12 -0
- data/test/keys/encrypted_public +4 -0
- data/test/keys/private +9 -0
- data/test/keys/public +4 -0
- data/test/rsa_key_test.rb +11 -0
- data/test/schema.rb +10 -0
- data/test/sha_sentry_test.rb +35 -0
- data/test/symmetric_sentry_callback_test.rb +38 -0
- data/test/symmetric_sentry_test.rb +37 -0
- data/test/tests.rb +2 -0
- metadata +97 -0
@@ -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,82 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "stderr-sentry"
|
8
|
+
s.version = "0.5.4"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Josh Fenio"]
|
12
|
+
s.date = %q{2010-04-02}
|
13
|
+
s.description = %q{Asymmetric / Symmetric encryption of active record fields}
|
14
|
+
s.email = %q{stderr@truedat.org}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gemspec",
|
20
|
+
".gitignore",
|
21
|
+
"CHANGELOG",
|
22
|
+
"MIT-LICENSE",
|
23
|
+
"README",
|
24
|
+
"RUNNING_UNIT_TESTS",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"init.rb",
|
28
|
+
"lib/active_record/sentry.rb",
|
29
|
+
"lib/sentry.rb",
|
30
|
+
"lib/sentry/asymmetric_sentry.rb",
|
31
|
+
"lib/sentry/asymmetric_sentry_callback.rb",
|
32
|
+
"lib/sentry/sha_sentry.rb",
|
33
|
+
"lib/sentry/symmetric_sentry.rb",
|
34
|
+
"lib/sentry/symmetric_sentry_callback.rb",
|
35
|
+
"sentry.gemspec",
|
36
|
+
"tasks/sentry.rake",
|
37
|
+
"test/abstract_unit.rb",
|
38
|
+
"test/asymmetric_sentry_callback_test.rb",
|
39
|
+
"test/asymmetric_sentry_test.rb",
|
40
|
+
"test/database.yml",
|
41
|
+
"test/fixtures/user.rb",
|
42
|
+
"test/fixtures/users.yml",
|
43
|
+
"test/keys/encrypted_private",
|
44
|
+
"test/keys/encrypted_public",
|
45
|
+
"test/keys/private",
|
46
|
+
"test/keys/public",
|
47
|
+
"test/rsa_key_test.rb",
|
48
|
+
"test/schema.rb",
|
49
|
+
"test/sha_sentry_test.rb",
|
50
|
+
"test/symmetric_sentry_callback_test.rb",
|
51
|
+
"test/symmetric_sentry_test.rb",
|
52
|
+
"test/tests.rb"
|
53
|
+
]
|
54
|
+
s.homepage = %q{http://github.com/pivotal/sentry}
|
55
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
56
|
+
s.require_paths = ["lib"]
|
57
|
+
s.rubygems_version = %q{1.3.5}
|
58
|
+
s.summary = %q{Asymmetric encryption of active record fields}
|
59
|
+
s.test_files = [
|
60
|
+
"test/abstract_unit.rb",
|
61
|
+
"test/asymmetric_sentry_callback_test.rb",
|
62
|
+
"test/asymmetric_sentry_test.rb",
|
63
|
+
"test/fixtures/user.rb",
|
64
|
+
"test/rsa_key_test.rb",
|
65
|
+
"test/schema.rb",
|
66
|
+
"test/sha_sentry_test.rb",
|
67
|
+
"test/symmetric_sentry_callback_test.rb",
|
68
|
+
"test/symmetric_sentry_test.rb",
|
69
|
+
"test/tests.rb"
|
70
|
+
]
|
71
|
+
|
72
|
+
if s.respond_to? :specification_version then
|
73
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
74
|
+
s.specification_version = 3
|
75
|
+
|
76
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
77
|
+
else
|
78
|
+
end
|
79
|
+
else
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
data/tasks/sentry.rake
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'sentry'
|
2
|
+
|
3
|
+
desc "Creates a private/public key for asymmetric encryption: rake sentry_key PUB=/path/to/public.key PRIV=/path/to/priv.key [KEY=secret]"
|
4
|
+
task :sentry_key do
|
5
|
+
Sentry::AsymmetricSentry.save_random_rsa_key(
|
6
|
+
ENV['PRIV'] || 'private.key',
|
7
|
+
ENV['PUB'] || 'public.key',
|
8
|
+
:key => ENV['KEY'])
|
9
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'active_record'
|
6
|
+
require 'active_record/fixtures'
|
7
|
+
require 'active_support/test_case'
|
8
|
+
#require 'active_support/binding_of_caller'
|
9
|
+
#require 'active_support/breakpoint'
|
10
|
+
require "#{File.dirname(__FILE__)}/../lib/sentry"
|
11
|
+
|
12
|
+
config_location = File.dirname(__FILE__) + '/database.yml'
|
13
|
+
|
14
|
+
config = YAML::load(IO.read(config_location))
|
15
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
16
|
+
ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'mysql'])
|
17
|
+
ActiveRecord::Base.configurations["test"] = "lolcatz"
|
18
|
+
|
19
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
20
|
+
|
21
|
+
class ActiveSupport::TestCase #:nodoc:
|
22
|
+
include ActiveRecord::TestFixtures
|
23
|
+
#def create_fixtures(*table_names)
|
24
|
+
# if block_given?
|
25
|
+
# Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names) { yield }
|
26
|
+
# else
|
27
|
+
# Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names)
|
28
|
+
# end
|
29
|
+
#end
|
30
|
+
|
31
|
+
self.use_instantiated_fixtures = false
|
32
|
+
self.use_transactional_fixtures = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_fixtures(*table_names, &block)
|
36
|
+
Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
ActiveSupport::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
42
|
+
ActiveSupport::TestCase.use_instantiated_fixtures = true
|
43
|
+
ActiveSupport::TestCase.use_transactional_fixtures = (ENV['AR_TX_FIXTURES'] == "yes")
|
44
|
+
$LOAD_PATH.unshift(ActiveSupport::TestCase.fixture_path)
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
require 'fixtures/user'
|
3
|
+
|
4
|
+
class AsymmetricSentryCallbackTest < ActiveSupport::TestCase
|
5
|
+
fixtures :users
|
6
|
+
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
@str = 'sentry'
|
10
|
+
@key = 'secret'
|
11
|
+
@public_key_file = File.dirname(__FILE__) + '/keys/public'
|
12
|
+
@private_key_file = File.dirname(__FILE__) + '/keys/private'
|
13
|
+
@encrypted_public_key_file = File.dirname(__FILE__) + '/keys/encrypted_public'
|
14
|
+
@encrypted_private_key_file = File.dirname(__FILE__) + '/keys/encrypted_private'
|
15
|
+
|
16
|
+
@orig = 'sentry'
|
17
|
+
Sentry::AsymmetricSentry.default_public_key_file = @public_key_file
|
18
|
+
Sentry::AsymmetricSentry.default_private_key_file = @private_key_file
|
19
|
+
Sentry::SymmetricSentry.default_key = @key
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown
|
23
|
+
super
|
24
|
+
Sentry.default_key = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_encryption_should_use_default_key_when_present
|
28
|
+
use_encrypted_keys
|
29
|
+
|
30
|
+
assert_nil users(:user_2).creditcard
|
31
|
+
Sentry.default_key = @key
|
32
|
+
|
33
|
+
assert_equal @orig, users(:user_2).creditcard
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_encrypt_for_sentry
|
37
|
+
assert_not_nil User.encrypt_for_sentry("hello")
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_encryption_with_random_padding
|
41
|
+
# system works with unsaved record
|
42
|
+
u = User.new :login => 'jones'
|
43
|
+
u.creditcard = @orig
|
44
|
+
assert_equal @orig, u.creditcard
|
45
|
+
u.save!
|
46
|
+
|
47
|
+
# reload after save and check the decrypt works
|
48
|
+
u = User.find(u.id)
|
49
|
+
assert_equal @orig, u.creditcard
|
50
|
+
original_crypttext = u.crypted_creditcard
|
51
|
+
|
52
|
+
# set to same plaintext
|
53
|
+
u.creditcard = @orig
|
54
|
+
u.save!
|
55
|
+
|
56
|
+
# expect different crypttext (due to random padding)
|
57
|
+
assert_not_equal original_crypttext, u.crypted_creditcard
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_should_handle_nils
|
61
|
+
u = User.create :login => 'john'
|
62
|
+
u.creditcard = nil
|
63
|
+
assert u.save
|
64
|
+
assert u.crypted_creditcard.nil?
|
65
|
+
assert u.creditcard.nil?
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_should_encrypt_creditcard
|
69
|
+
u = User.create :login => 'jones'
|
70
|
+
u.creditcard = @orig
|
71
|
+
assert u.save
|
72
|
+
assert !u.crypted_creditcard.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_should_deal_with_before_typecast
|
76
|
+
u = User.create :login => 'jones'
|
77
|
+
u.creditcard = "123123"
|
78
|
+
assert_equal "123123", u.creditcard_before_type_cast
|
79
|
+
assert u.save
|
80
|
+
u.reload
|
81
|
+
assert_equal "123123", u.creditcard_before_type_cast
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_should_decrypt_creditcard
|
85
|
+
assert_equal @orig, users(:user_1).creditcard
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_should_not_decrypt_encrypted_creditcard_with_invalid_key
|
89
|
+
assert_nil users(:user_2).creditcard
|
90
|
+
assert_nil users(:user_2).creditcard(@key)
|
91
|
+
use_encrypted_keys
|
92
|
+
assert_nil users(:user_1).creditcard
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_should_not_decrypt_encrypted_creditcard
|
96
|
+
use_encrypted_keys
|
97
|
+
assert_nil users(:user_2).creditcard
|
98
|
+
assert_nil users(:user_2).creditcard('other secret')
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_do_encryption
|
102
|
+
use_encrypted_keys
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_should_encrypt_encrypted_creditcard
|
106
|
+
use_encrypted_keys
|
107
|
+
u = User.create :login => 'jones'
|
108
|
+
u.creditcard = @orig
|
109
|
+
assert u.save
|
110
|
+
assert !u.crypted_creditcard.empty?
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_should_decrypt_encrypted_creditcard
|
114
|
+
use_encrypted_keys
|
115
|
+
assert_equal @orig, users(:user_2).creditcard(@key)
|
116
|
+
end
|
117
|
+
|
118
|
+
def use_encrypted_keys
|
119
|
+
Sentry::AsymmetricSentry.default_public_key_file = @encrypted_public_key_file
|
120
|
+
Sentry::AsymmetricSentry.default_private_key_file = @encrypted_private_key_file
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class AsymmetricSentryTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
Sentry::AsymmetricSentry.default_public_key_file = nil
|
6
|
+
Sentry::AsymmetricSentry.default_private_key_file = nil
|
7
|
+
@str = 'sentry'
|
8
|
+
@key = 'secret'
|
9
|
+
@public_key_file = File.dirname(__FILE__) + '/keys/public'
|
10
|
+
@private_key_file = File.dirname(__FILE__) + '/keys/private'
|
11
|
+
@encrypted_public_key_file = File.dirname(__FILE__) + '/keys/encrypted_public'
|
12
|
+
@encrypted_private_key_file = File.dirname(__FILE__) + '/keys/encrypted_private'
|
13
|
+
@sentry = Sentry::AsymmetricSentry.new
|
14
|
+
|
15
|
+
@orig = 'sentry'
|
16
|
+
@data = "vYfMxtVB8ezXmQKSNqTC9sPgi8TbsYRxWd7DVbpprzyuEdZ7gftJ/0IXsbXm\nXCU08bTAl0uEFm7dau+eJMXEJg==\n"
|
17
|
+
@encrypted_data = "q2obYAITmK93ylzVS01mJx1jSlnmylMX15nFpb4uKesVgnqvtzBRHZ/SK+Nm\nEzceIoAcJc3DHosVa4VUE/aK/A==\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_decrypt_files
|
21
|
+
set_key_files @public_key_file, @private_key_file
|
22
|
+
assert_equal @orig, @sentry.decrypt_from_base64(@data)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_decrypt_files_with_encrypted_key
|
26
|
+
set_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
27
|
+
assert_equal @orig, @sentry.decrypt_from_base64(@encrypted_data, @key)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_should_read_key_files
|
31
|
+
assert !@sentry.public?
|
32
|
+
assert !@sentry.private?
|
33
|
+
set_key_files @public_key_file, @private_key_file
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_should_read_encrypted_key_files
|
37
|
+
assert !@sentry.public?
|
38
|
+
assert !@sentry.private?
|
39
|
+
set_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_should_decrypt_files_with_default_key
|
43
|
+
set_default_key_files @public_key_file, @private_key_file
|
44
|
+
assert_equal @orig, @sentry.decrypt_from_base64(@data)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_should_decrypt_files_with_default_encrypted_key
|
48
|
+
set_default_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
49
|
+
assert_equal @orig, @sentry.decrypt_from_base64(@encrypted_data, @key)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_decrypt_block_by_block_for_large_data
|
53
|
+
set_default_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
54
|
+
large_data = "asdf" * 2048
|
55
|
+
encrypted = @sentry.encrypt_large_to_base64(large_data)
|
56
|
+
assert_not_equal large_data, encrypted
|
57
|
+
assert_equal large_data, @sentry.decrypt_large_from_base64(encrypted, @key)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_should_decrypt_files_with_default_key_using_class_method
|
61
|
+
set_default_key_files @public_key_file, @private_key_file
|
62
|
+
assert_equal @orig, Sentry::AsymmetricSentry.decrypt_from_base64(@data)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_should_decrypt_files_with_default_encrypted_key_using_class_method
|
66
|
+
set_default_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
67
|
+
assert_equal @orig, Sentry::AsymmetricSentry.decrypt_from_base64(@encrypted_data, @key)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_should_read_key_files_with_default_key
|
71
|
+
assert !@sentry.public?
|
72
|
+
assert !@sentry.private?
|
73
|
+
set_default_key_files @public_key_file, @private_key_file
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_should_read_encrypted_key_files_with_default_key
|
77
|
+
assert !@sentry.public?
|
78
|
+
assert !@sentry.private?
|
79
|
+
set_default_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def set_key_files(public_key, private_key)
|
85
|
+
@sentry.public_key_file = public_key
|
86
|
+
@sentry.private_key_file = private_key
|
87
|
+
assert @sentry.private?
|
88
|
+
assert @sentry.public?
|
89
|
+
end
|
90
|
+
|
91
|
+
def set_default_key_files(public_key, private_key)
|
92
|
+
Sentry::AsymmetricSentry.default_public_key_file = public_key
|
93
|
+
Sentry::AsymmetricSentry.default_private_key_file = private_key
|
94
|
+
assert @sentry.private?
|
95
|
+
assert @sentry.public?
|
96
|
+
end
|
97
|
+
end
|
data/test/database.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
sqlite:
|
2
|
+
:adapter: sqlite
|
3
|
+
:dbfile: sentry_plugin.sqlite.db
|
4
|
+
sqlite3:
|
5
|
+
:adapter: sqlite3
|
6
|
+
:dbfile: sentry_plugin.sqlite3.db
|
7
|
+
postgresql:
|
8
|
+
:adapter: postgresql
|
9
|
+
:username: postgres
|
10
|
+
:password: postgres
|
11
|
+
:database: sentry_plugin_test
|
12
|
+
:min_messages: ERROR
|
13
|
+
mysql:
|
14
|
+
:adapter: mysql
|
15
|
+
:host: localhost
|
16
|
+
:username: root
|
17
|
+
:password: password
|
18
|
+
:database: sentry_plugin_test
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class User < ActiveRecord::Base
|
2
|
+
#define_read_methods
|
3
|
+
asymmetrically_encrypts :creditcard
|
4
|
+
|
5
|
+
#def self.validates_password
|
6
|
+
# validates_presence_of :password
|
7
|
+
# validates_presence_of :password, :on => :create
|
8
|
+
# validates_length_of :password, :in => 4..40
|
9
|
+
#end
|
10
|
+
end
|
11
|
+
|
12
|
+
#class ShaUser < User
|
13
|
+
# validates_password
|
14
|
+
# validates_confirmation_of :password
|
15
|
+
# generates_crypted :password # sha is used by default
|
16
|
+
#end
|
17
|
+
#
|
18
|
+
#class DangerousUser < User # no password confirmation
|
19
|
+
## validates_password
|
20
|
+
# generates_crypted :password
|
21
|
+
#end
|
22
|
+
#
|
23
|
+
#class SymmetricUser < User
|
24
|
+
# validates_password
|
25
|
+
# generates_crypted :password, :mode => :symmetric
|
26
|
+
#end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
user_1:
|
2
|
+
id: 1
|
3
|
+
login: bob
|
4
|
+
password: "0XlmUuNpE2k=\n"
|
5
|
+
creditcard: "CBUI2TcYh/ATRB7fYpDBb0t1ifOWPb5jfpO2M8Zy9D/8Gua6/uA+ILHwKtGY\nOgrooPYSxwpBzEZoH18mXqJE7yk=\n" # "sentry" with 8 characters of prepadding
|
6
|
+
user_2:
|
7
|
+
id: 2
|
8
|
+
login: fred
|
9
|
+
creditcard: "CEUx1Ufxi7leQVp0xHhMWKqBcvrb0p3VvX5rqJBXSwddH+Alscs73TEX8Ctn\n9WnV5Ii8txpa20UfM3h5msLpm20=\n" # "sentry" with 8 different characters of prepadding
|
@@ -0,0 +1,12 @@
|
|
1
|
+
OBNa1q8kbx8pyZZjIpr/pZV0oulE2czh5JlPW/13XsBvoz+A2zxA9gchhi6c
|
2
|
+
3yvfqgcZdojcsep+IiTqeg3gOPB2xNbedpP1lm+9tEfgdb9r1CLzRcURh7Hg
|
3
|
+
ufWgyEkS0lloz/YLy4hg9YDKetFNF9fnrk3xVwZPwFVuk4l/Unw1FTXLHsrq
|
4
|
+
KG27cR8mvNOow4bk4LVhk/avFSM85m3ITySEnyJsQQDzsI/RrWcQ7Js+8Ynv
|
5
|
+
esN51E/T0CYtkMEne2zSaD5qUTJlQ7Qtn4UUeZkpYjn4xQZPxw4OjL6zofg7
|
6
|
+
lsqElSv1/qP3QI8aKcQQklVsHRc5AgsxOFX4J6g6lo4kOGOwn0Ex8IRDfOej
|
7
|
+
pq4SUDh9IXz+6FBieQrObB/xEsKysVwRSzXre6ObHlPFsigg5ekFPyCv5ZTz
|
8
|
+
0iP8+xe/FJRrYdR3r3F5pRkOy0pw9EqlrLjmOx3/fgxhLq8FWmcSBbH3h3SG
|
9
|
+
GkJlfHNjF77FTJjnHKzRS+5VpdW4IHbsjL+NlI1z9Ol//czYvSGv85NdJvkq
|
10
|
+
PmH3o0+uYdwY5PeSMOPV21nJ3dwiKlm5IMFasL3C5yVJNVTVZTS7vWdcgZ4U
|
11
|
+
XfWQ9Y266ibbqXPluv4nxt1+kgjxmPbjPdYrlB5t7a2+unzT3oE3f4VGOG+k
|
12
|
+
YqFg0ErHN+fu
|