encrypted_cookie_store-instructure 1.0.8 → 1.1.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.
- data/encrypted_cookie_store-instructure.gemspec +9 -3
- data/lib/encrypted_cookie_store.rb +159 -169
- metadata +90 -8
- checksums.yaml +0 -15
@@ -1,9 +1,9 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = %q{encrypted_cookie_store-instructure}
|
3
|
-
s.version = "1.0
|
3
|
+
s.version = "1.1.0"
|
4
4
|
|
5
5
|
s.authors = ["Cody Cutrer", "Jacob Fugal", "James Williams"]
|
6
|
-
s.date = %q{2013-
|
6
|
+
s.date = %q{2013-11-13}
|
7
7
|
s.extra_rdoc_files = [
|
8
8
|
"LICENSE.txt"
|
9
9
|
]
|
@@ -15,6 +15,12 @@ Gem::Specification.new do |s|
|
|
15
15
|
]
|
16
16
|
s.homepage = %q{http://github.com/ccutrer/encrypted_cookie_store}
|
17
17
|
s.require_paths = ["lib"]
|
18
|
-
s.summary = %q{EncryptedCookieStore for Ruby on Rails 2
|
18
|
+
s.summary = %q{EncryptedCookieStore for Ruby on Rails 3.2}
|
19
19
|
s.description = %q{A secure version of Rails' built in CookieStore}
|
20
|
+
|
21
|
+
s.add_dependency "actionpack", "~> 3.2"
|
22
|
+
s.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
s.add_development_dependency "rake"
|
24
|
+
s.add_development_dependency "rspec-rails", "~> 2.0"
|
25
|
+
s.add_development_dependency "debugger"
|
20
26
|
end
|
@@ -1,204 +1,194 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
require 'zlib'
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
require 'active_support/core_ext/hash/deep_dup'
|
5
|
+
require 'active_support/core_ext/numeric/time'
|
6
|
+
require 'action_dispatch'
|
7
|
+
|
8
|
+
module ActionDispatch
|
9
|
+
module Session
|
10
|
+
class EncryptedCookieStore < CookieStore
|
11
|
+
class << self
|
12
|
+
attr_accessor :data_cipher_type
|
13
|
+
end
|
14
|
+
self.data_cipher_type = "aes-128-cbc".freeze
|
6
15
|
|
7
|
-
|
16
|
+
OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
|
8
17
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
18
|
+
def initialize(app, options = {})
|
19
|
+
@digest = options.delete(:digest) || 'SHA1'
|
12
20
|
|
13
|
-
|
14
|
-
|
15
|
-
def initialize(app, options = {})
|
16
|
-
options[:secret] = options[:secret].call if options[:secret].respond_to?(:call)
|
17
|
-
@logger = options[:logger]
|
18
|
-
ensure_encryption_key_secure(options[:secret])
|
19
|
-
@encryption_key = unhex(options[:secret]).freeze
|
20
|
-
@compress = options[:compress]
|
21
|
-
@compress = true if @compress.nil?
|
22
|
-
@data_cipher = OpenSSL::Cipher::Cipher.new(EncryptedCookieStore.data_cipher_type)
|
23
|
-
@options = options
|
24
|
-
options[:refresh_interval] ||= 5.minutes
|
25
|
-
super(app, options)
|
26
|
-
end
|
21
|
+
@compress = options[:compress]
|
22
|
+
@compress = true if @compress.nil?
|
27
23
|
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
@secret = options.delete(:secret)
|
25
|
+
@secret = @secret.call if @secret.respond_to?(:call)
|
26
|
+
@secret.freeze
|
27
|
+
@encryption_key = unhex(@secret).freeze
|
28
|
+
ensure_encryption_key_secure
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
old_session_data = Marshal.load(raw_old_session_data) if raw_old_session_data
|
35
|
-
env['encrypted_cookie_store.session_refreshed_at'] ||= session_refreshed_at(old_timestamp)
|
30
|
+
@data_cipher = OpenSSL::Cipher::Cipher.new(EncryptedCookieStore.data_cipher_type)
|
31
|
+
options[:refresh_interval] ||= 5.minutes
|
36
32
|
|
37
|
-
|
33
|
+
super(app, options)
|
34
|
+
end
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
private
|
37
|
+
|
38
|
+
# overrides method in ActionDispatch::Session::CookieStore
|
39
|
+
def unpacked_cookie_data(env)
|
40
|
+
env['encrypted_cookie_store.cookie'] ||= begin
|
41
|
+
stale_session_check! do
|
42
|
+
request = ActionDispatch::Request.new(env)
|
43
|
+
if data = unmarshal(request.cookie_jar.signed[@key])
|
44
|
+
data.stringify_keys!
|
45
|
+
end
|
46
|
+
data ||= {}
|
47
|
+
env['encrypted_cookie_store.original_cookie'] = data.deep_dup.except(:timestamp)
|
48
|
+
data
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
42
52
|
|
43
|
-
|
44
|
-
|
53
|
+
# overrides method in ActionDispatch::Session::CookieStore
|
54
|
+
def set_session(env, sid, session_data, options)
|
55
|
+
session_data = super
|
56
|
+
session_data.delete(:timestamp)
|
57
|
+
marshal(session_data, options)
|
58
|
+
end
|
45
59
|
|
46
|
-
|
47
|
-
|
60
|
+
# overrides method in Rack::Session::Cookie
|
61
|
+
def load_session(env)
|
62
|
+
if time = timestamp(env)
|
63
|
+
env['encrypted_cookie_store.session_refreshed_at'] ||= Time.at(time).utc
|
64
|
+
end
|
65
|
+
super
|
66
|
+
end
|
48
67
|
|
49
|
-
|
68
|
+
# overrides method in Rack::Session::Abstract::ID
|
69
|
+
def commit_session?(env, session, options)
|
70
|
+
can_commit = super
|
71
|
+
can_commit && (session_changed?(env, session) || refresh_session?(env, options))
|
72
|
+
end
|
50
73
|
|
51
|
-
old_session_data = nil if options[:expire_after] && old_timestamp && Time.now.utc.to_i > old_timestamp + options[:refresh_interval]
|
52
|
-
return [status, headers, body] if session_data == old_session_data
|
53
74
|
|
54
|
-
|
75
|
+
def timestamp(env)
|
76
|
+
unpacked_cookie_data(env)["timestamp"]
|
77
|
+
end
|
55
78
|
|
56
|
-
|
79
|
+
def session_changed?(env, session)
|
80
|
+
(session || {}).to_hash.stringify_keys.except(:timestamp) != (env['encrypted_cookie_store.original_cookie'] || {})
|
81
|
+
end
|
57
82
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
83
|
+
def refresh_session?(env, options)
|
84
|
+
if options[:expire_after] && options[:refresh_interval] && time = timestamp(env)
|
85
|
+
Time.now.utc.to_i > time + options[:refresh_interval]
|
86
|
+
else
|
87
|
+
false
|
88
|
+
end
|
62
89
|
end
|
63
90
|
|
64
|
-
|
65
|
-
|
91
|
+
def marshal(data, options={})
|
92
|
+
@data_cipher.encrypt
|
93
|
+
@data_cipher.key = @encryption_key
|
66
94
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
95
|
+
session_data = Marshal.dump(data)
|
96
|
+
iv = @data_cipher.random_iv
|
97
|
+
if @compress
|
98
|
+
compressed_session_data = deflate(session_data, 5)
|
99
|
+
compressed_session_data = session_data if compressed_session_data.length >= session_data.length
|
100
|
+
else
|
101
|
+
compressed_session_data = session_data
|
102
|
+
end
|
103
|
+
encrypted_session_data = @data_cipher.update(compressed_session_data) << @data_cipher.final
|
104
|
+
timestamp = Time.now.utc.to_i if options[:expire_after]
|
105
|
+
digest = OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new(@digest), @secret, session_data + timestamp.to_s)
|
73
106
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
session_data = Marshal.dump(session)
|
79
|
-
iv = @data_cipher.random_iv
|
80
|
-
if @compress
|
81
|
-
compressed_session_data = deflate(session_data, 5)
|
82
|
-
compressed_session_data = session_data if compressed_session_data.length >= session_data.length
|
83
|
-
else
|
84
|
-
compressed_session_data = session_data
|
85
|
-
end
|
86
|
-
encrypted_session_data = @data_cipher.update(compressed_session_data) << @data_cipher.final
|
87
|
-
timestamp = Time.now.utc.to_i if @options[:expire_after]
|
88
|
-
digest = OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new(@digest), secret, session_data + timestamp.to_s)
|
107
|
+
result = "#{base64(iv)}#{compressed_session_data == session_data ? '.' : ' '}#{base64(encrypted_session_data)}.#{base64(digest)}"
|
108
|
+
result << ".#{base64([timestamp].pack('N'))}" if options[:expire_after]
|
109
|
+
result
|
110
|
+
end
|
89
111
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
112
|
+
def unmarshal(data, options={})
|
113
|
+
return nil unless data
|
114
|
+
compressed = !!data.index(' ')
|
115
|
+
b64_iv, b64_encrypted_session_data, b64_digest, b64_timestamp = data.split(/\.| /, 4)
|
116
|
+
if b64_iv && b64_encrypted_session_data && b64_digest
|
117
|
+
iv = unbase64(b64_iv)
|
118
|
+
encrypted_session_data = unbase64(b64_encrypted_session_data)
|
119
|
+
digest = unbase64(b64_digest)
|
120
|
+
timestamp = unbase64(b64_timestamp).unpack('N').first if b64_timestamp
|
121
|
+
|
122
|
+
@data_cipher.decrypt
|
123
|
+
@data_cipher.key = @encryption_key
|
124
|
+
@data_cipher.iv = iv
|
125
|
+
session_data = @data_cipher.update(encrypted_session_data) << @data_cipher.final
|
126
|
+
session_data = inflate(session_data) if compressed
|
127
|
+
return nil unless digest == OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new(@digest), @secret, session_data + timestamp.to_s)
|
128
|
+
if options[:expire_after]
|
129
|
+
return nil unless timestamp && Time.now.utc.to_i <= timestamp + options[:expire_after]
|
130
|
+
end
|
131
|
+
|
132
|
+
loaded_data = Marshal.load(session_data) || nil
|
133
|
+
loaded_data[:timestamp] = timestamp if loaded_data && timestamp
|
134
|
+
loaded_data
|
135
|
+
else
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
rescue Zlib::DataError, OpenSSLCipherError
|
139
|
+
nil
|
140
|
+
end
|
94
141
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
timestamp = unbase64(b64_timestamp).unpack('N').first if b64_timestamp
|
104
|
-
|
105
|
-
@data_cipher.decrypt
|
106
|
-
@data_cipher.key = @encryption_key
|
107
|
-
@data_cipher.iv = iv
|
108
|
-
session_data = @data_cipher.update(encrypted_session_data) << @data_cipher.final
|
109
|
-
session_data = inflate(session_data) if compressed
|
110
|
-
return [nil, nil, nil] unless digest == OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new(@digest), secret, session_data + timestamp.to_s)
|
111
|
-
if @options[:expire_after]
|
112
|
-
return [nil, nil, nil] unless timestamp
|
113
|
-
return [nil, nil, timestamp] unless Time.now.utc.to_i - timestamp < @options[:expire_after]
|
142
|
+
# To prevent users from using an insecure encryption key like "Password" we make sure that the
|
143
|
+
# encryption key they've provided is at least 30 characters in length.
|
144
|
+
def ensure_encryption_key_secure
|
145
|
+
if @encryption_key.blank?
|
146
|
+
raise ArgumentError, "An encryption key is required for encrypting the " +
|
147
|
+
"cookie session data. Please set config.action_controller.session = { " +
|
148
|
+
"..., :encryption_key => \"some random string of at least " +
|
149
|
+
"16 bytes\", ... } in config/environment.rb"
|
114
150
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
151
|
+
|
152
|
+
if @encryption_key.size < 16 * 2
|
153
|
+
raise ArgumentError, "The EncryptedCookieStore encryption key must be a " +
|
154
|
+
"hexadecimal string of at least 16 bytes. " +
|
155
|
+
"The value that you've provided, \"#{@encryption_key}\", is " +
|
156
|
+
"#{@encryption_key.size / 2} bytes. You could use the following (randomly " +
|
157
|
+
"generated) string as encryption key: " +
|
158
|
+
ActiveSupport::SecureRandom.hex(16)
|
120
159
|
end
|
121
|
-
[loaded_data, session_data, timestamp]
|
122
|
-
else
|
123
|
-
[nil, nil, nil]
|
124
160
|
end
|
125
|
-
else
|
126
|
-
[nil, nil, nil]
|
127
|
-
end
|
128
|
-
rescue Zlib::DataError
|
129
|
-
[nil, nil, nil]
|
130
|
-
rescue OpenSSLCipherError
|
131
|
-
[nil, nil, nil]
|
132
|
-
end
|
133
161
|
|
134
|
-
|
135
|
-
|
136
|
-
stale_session_check! do
|
137
|
-
request = Rack::Request.new(env)
|
138
|
-
session_data = request.cookies[@key]
|
139
|
-
unmarshal(session_data) || {}
|
162
|
+
def base64(data)
|
163
|
+
::Base64.encode64(data).tr('+/', '-_').gsub(/=|\n/, '')
|
140
164
|
end
|
141
|
-
end
|
142
|
-
end
|
143
165
|
|
144
|
-
|
145
|
-
|
146
|
-
|
166
|
+
def unbase64(data)
|
167
|
+
::Base64.decode64(data.tr('-_', '+/').ljust((data.length + 4 - 1) / 4 * 4, '='))
|
168
|
+
end
|
147
169
|
|
148
|
-
|
149
|
-
|
150
|
-
|
170
|
+
# compress
|
171
|
+
def deflate(string, level)
|
172
|
+
z = Zlib::Deflate.new(level)
|
173
|
+
dst = z.deflate(string, Zlib::FINISH)
|
174
|
+
z.close
|
175
|
+
dst
|
176
|
+
end
|
151
177
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
178
|
+
# decompress
|
179
|
+
def inflate(string)
|
180
|
+
zstream = Zlib::Inflate.new
|
181
|
+
buf = zstream.inflate(string)
|
182
|
+
zstream.finish
|
183
|
+
zstream.close
|
184
|
+
buf
|
185
|
+
end
|
161
186
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
"The value that you've provided, \"#{encryption_key}\", is " +
|
166
|
-
"#{encryption_key.size / 2} bytes. You could use the following (randomly " +
|
167
|
-
"generated) string as encryption key: " +
|
168
|
-
ActiveSupport::SecureRandom.hex(16)
|
187
|
+
def unhex(hex_data)
|
188
|
+
[hex_data].pack("H*")
|
189
|
+
end
|
169
190
|
end
|
170
191
|
end
|
171
|
-
|
172
|
-
def verifier_for(secret, digest)
|
173
|
-
nil
|
174
|
-
end
|
175
|
-
|
176
|
-
def base64(data)
|
177
|
-
ActiveSupport::Base64.encode64(data).tr('+/', '-_').gsub(/=|\n/, '')
|
178
|
-
end
|
179
|
-
|
180
|
-
def unbase64(data)
|
181
|
-
ActiveSupport::Base64.decode64(data.tr('-_', '+/').ljust((data.length + 4 - 1) / 4 * 4, '='))
|
182
|
-
end
|
183
|
-
|
184
|
-
# aka compress
|
185
|
-
def deflate(string, level)
|
186
|
-
z = Zlib::Deflate.new(level)
|
187
|
-
dst = z.deflate(string, Zlib::FINISH)
|
188
|
-
z.close
|
189
|
-
dst
|
190
|
-
end
|
191
|
-
|
192
|
-
# aka decompress
|
193
|
-
def inflate(string)
|
194
|
-
zstream = Zlib::Inflate.new
|
195
|
-
buf = zstream.inflate(string)
|
196
|
-
zstream.finish
|
197
|
-
zstream.close
|
198
|
-
buf
|
199
|
-
end
|
200
|
-
|
201
|
-
def unhex(hex_data)
|
202
|
-
[hex_data].pack("H*")
|
203
|
-
end
|
204
192
|
end
|
193
|
+
|
194
|
+
EncryptedCookieStore = ActionDispatch::Session::EncryptedCookieStore
|
metadata
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: encrypted_cookie_store-instructure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Cody Cutrer
|
@@ -10,8 +11,88 @@ authors:
|
|
10
11
|
autorequire:
|
11
12
|
bindir: bin
|
12
13
|
cert_chain: []
|
13
|
-
date: 2013-
|
14
|
-
dependencies:
|
14
|
+
date: 2013-11-13 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: actionpack
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '3.2'
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ~>
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '3.2'
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: bundler
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ~>
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.3'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rake
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
- !ruby/object:Gem::Dependency
|
65
|
+
name: rspec-rails
|
66
|
+
requirement: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ~>
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '2.0'
|
72
|
+
type: :development
|
73
|
+
prerelease: false
|
74
|
+
version_requirements: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ~>
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '2.0'
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: debugger
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
15
96
|
description: A secure version of Rails' built in CookieStore
|
16
97
|
email:
|
17
98
|
executables: []
|
@@ -21,30 +102,31 @@ extra_rdoc_files:
|
|
21
102
|
files:
|
22
103
|
- LICENSE.txt
|
23
104
|
- README.markdown
|
24
|
-
- encrypted_cookie_store-instructure.gemspec
|
25
105
|
- lib/encrypted_cookie_store.rb
|
106
|
+
- encrypted_cookie_store-instructure.gemspec
|
26
107
|
homepage: http://github.com/ccutrer/encrypted_cookie_store
|
27
108
|
licenses: []
|
28
|
-
metadata: {}
|
29
109
|
post_install_message:
|
30
110
|
rdoc_options: []
|
31
111
|
require_paths:
|
32
112
|
- lib
|
33
113
|
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
34
115
|
requirements:
|
35
116
|
- - ! '>='
|
36
117
|
- !ruby/object:Gem::Version
|
37
118
|
version: '0'
|
38
119
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
39
121
|
requirements:
|
40
122
|
- - ! '>='
|
41
123
|
- !ruby/object:Gem::Version
|
42
124
|
version: '0'
|
43
125
|
requirements: []
|
44
126
|
rubyforge_project:
|
45
|
-
rubygems_version:
|
127
|
+
rubygems_version: 1.8.23
|
46
128
|
signing_key:
|
47
|
-
specification_version:
|
48
|
-
summary: EncryptedCookieStore for Ruby on Rails 2
|
129
|
+
specification_version: 3
|
130
|
+
summary: EncryptedCookieStore for Ruby on Rails 3.2
|
49
131
|
test_files: []
|
50
132
|
has_rdoc:
|
checksums.yaml
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
---
|
2
|
-
!binary "U0hBMQ==":
|
3
|
-
metadata.gz: !binary |-
|
4
|
-
OTNlMTI1ODU0MzA1OWU5NGFmOTFkMTk4NjEyNTVjNzVkZjAxOTAxNA==
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
N2Q3MGExZTg4MDgyMWZhMmMxNjc2ZWFlNGVhY2I5MTRmZmRjNDM5OQ==
|
7
|
-
SHA512:
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
MDNiZjdkOTQ3YzMyMGU2N2I5NzI0Zjg5N2ExNGJmM2M5ZjdkNjFlNTFlZThm
|
10
|
-
MWRjNjU1ZGFiNWZlNjRlZWFjMWMzY2VlYjVjZjZlOGQzMWNjMjdjMzM4Zjk1
|
11
|
-
MDA5Yzc5YmFhZDg2YzE5ZTA1MGExOWYyODEyZDY0ZDc4YWU1MGI=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZWQwNzg1MjFlZWY0ZGRjYzBlZWM0MWZlMzJiNmJhNDc4NDY0OGIwMTJmYzRi
|
14
|
-
NzU1OTlhYzQ5Yzg0MDQ5NDFlMjM2NTE4OTFhODE2MGU4ZTU4ZDVjZDlkMTk1
|
15
|
-
ODExNjQzZjg1MDcwOTM1MzdhM2JiOTNlODM2NDc0YTFlYjRlMTg=
|