ruby-openid2 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +136 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/CONTRIBUTING.md +54 -0
- data/LICENSE.txt +210 -0
- data/README.md +81 -0
- data/SECURITY.md +15 -0
- data/lib/hmac/hmac.rb +110 -0
- data/lib/hmac/sha1.rb +11 -0
- data/lib/hmac/sha2.rb +25 -0
- data/lib/openid/association.rb +246 -0
- data/lib/openid/consumer/associationmanager.rb +354 -0
- data/lib/openid/consumer/checkid_request.rb +179 -0
- data/lib/openid/consumer/discovery.rb +516 -0
- data/lib/openid/consumer/discovery_manager.rb +144 -0
- data/lib/openid/consumer/html_parse.rb +142 -0
- data/lib/openid/consumer/idres.rb +513 -0
- data/lib/openid/consumer/responses.rb +147 -0
- data/lib/openid/consumer/session.rb +36 -0
- data/lib/openid/consumer.rb +406 -0
- data/lib/openid/cryptutil.rb +112 -0
- data/lib/openid/dh.rb +84 -0
- data/lib/openid/extension.rb +38 -0
- data/lib/openid/extensions/ax.rb +552 -0
- data/lib/openid/extensions/oauth.rb +88 -0
- data/lib/openid/extensions/pape.rb +170 -0
- data/lib/openid/extensions/sreg.rb +268 -0
- data/lib/openid/extensions/ui.rb +49 -0
- data/lib/openid/fetchers.rb +277 -0
- data/lib/openid/kvform.rb +113 -0
- data/lib/openid/kvpost.rb +62 -0
- data/lib/openid/message.rb +555 -0
- data/lib/openid/protocolerror.rb +7 -0
- data/lib/openid/server.rb +1571 -0
- data/lib/openid/store/filesystem.rb +260 -0
- data/lib/openid/store/interface.rb +73 -0
- data/lib/openid/store/memcache.rb +109 -0
- data/lib/openid/store/memory.rb +79 -0
- data/lib/openid/store/nonce.rb +72 -0
- data/lib/openid/trustroot.rb +597 -0
- data/lib/openid/urinorm.rb +72 -0
- data/lib/openid/util.rb +119 -0
- data/lib/openid/version.rb +5 -0
- data/lib/openid/yadis/accept.rb +141 -0
- data/lib/openid/yadis/constants.rb +16 -0
- data/lib/openid/yadis/discovery.rb +151 -0
- data/lib/openid/yadis/filters.rb +192 -0
- data/lib/openid/yadis/htmltokenizer.rb +290 -0
- data/lib/openid/yadis/parsehtml.rb +50 -0
- data/lib/openid/yadis/services.rb +44 -0
- data/lib/openid/yadis/xrds.rb +160 -0
- data/lib/openid/yadis/xri.rb +86 -0
- data/lib/openid/yadis/xrires.rb +87 -0
- data/lib/openid.rb +27 -0
- data/lib/ruby-openid.rb +1 -0
- data.tar.gz.sig +0 -0
- metadata +331 -0
- metadata.gz.sig +0 -0
data/lib/hmac/hmac.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Copyright (C) 2001 Daiki Ueno <ueno@unixuser.org>
|
2
|
+
# This library is distributed under the terms of the Ruby license.
|
3
|
+
|
4
|
+
# This module provides common interface to HMAC engines.
|
5
|
+
# HMAC standard is documented in RFC 2104:
|
6
|
+
#
|
7
|
+
# H. Krawczyk et al., "HMAC: Keyed-Hashing for Message Authentication",
|
8
|
+
# RFC 2104, February 1997
|
9
|
+
#
|
10
|
+
# These APIs are inspired by JCE 1.2's javax.crypto.Mac interface.
|
11
|
+
#
|
12
|
+
# <URL:http://java.sun.com/security/JCE1.2/spec/apidoc/javax/crypto/Mac.html>
|
13
|
+
|
14
|
+
module HMAC
|
15
|
+
class Base
|
16
|
+
def initialize(algorithm, block_size, output_length, key)
|
17
|
+
@algorithm = algorithm
|
18
|
+
@block_size = block_size
|
19
|
+
@output_length = output_length
|
20
|
+
@status = STATUS_UNDEFINED
|
21
|
+
@key_xor_ipad = ""
|
22
|
+
@key_xor_opad = ""
|
23
|
+
set_key(key) unless key.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def check_status
|
29
|
+
return if @status == STATUS_INITIALIZED
|
30
|
+
|
31
|
+
raise "The underlying hash algorithm has not yet been initialized."
|
32
|
+
end
|
33
|
+
|
34
|
+
public
|
35
|
+
|
36
|
+
def set_key(key)
|
37
|
+
# If key is longer than the block size, apply hash function
|
38
|
+
# to key and use the result as a real key.
|
39
|
+
key = @algorithm.digest(key) if key.size > @block_size
|
40
|
+
key_xor_ipad = "\x36" * @block_size
|
41
|
+
key_xor_opad = "\x5C" * @block_size
|
42
|
+
for i in 0..key.size - 1
|
43
|
+
key_xor_ipad[i] ^= key[i]
|
44
|
+
key_xor_opad[i] ^= key[i]
|
45
|
+
end
|
46
|
+
@key_xor_ipad = key_xor_ipad
|
47
|
+
@key_xor_opad = key_xor_opad
|
48
|
+
@md = @algorithm.new
|
49
|
+
@status = STATUS_INITIALIZED
|
50
|
+
end
|
51
|
+
|
52
|
+
def reset_key
|
53
|
+
@key_xor_ipad.gsub!(/./, "?")
|
54
|
+
@key_xor_opad.gsub!(/./, "?")
|
55
|
+
@key_xor_ipad[0..-1] = ""
|
56
|
+
@key_xor_opad[0..-1] = ""
|
57
|
+
@status = STATUS_UNDEFINED
|
58
|
+
end
|
59
|
+
|
60
|
+
def update(text)
|
61
|
+
check_status
|
62
|
+
# perform inner H
|
63
|
+
md = @algorithm.new
|
64
|
+
md.update(@key_xor_ipad)
|
65
|
+
md.update(text)
|
66
|
+
str = md.digest
|
67
|
+
# perform outer H
|
68
|
+
md = @algorithm.new
|
69
|
+
md.update(@key_xor_opad)
|
70
|
+
md.update(str)
|
71
|
+
@md = md
|
72
|
+
end
|
73
|
+
alias_method :<<, :update
|
74
|
+
|
75
|
+
def digest
|
76
|
+
check_status
|
77
|
+
@md.digest
|
78
|
+
end
|
79
|
+
|
80
|
+
def hexdigest
|
81
|
+
check_status
|
82
|
+
@md.hexdigest
|
83
|
+
end
|
84
|
+
alias_method :to_s, :hexdigest
|
85
|
+
|
86
|
+
# These two class methods below are safer than using above
|
87
|
+
# instance methods combinatorially because an instance will have
|
88
|
+
# held a key even if it's no longer in use.
|
89
|
+
def self.digest(key, text)
|
90
|
+
hmac = new(key)
|
91
|
+
hmac.update(text)
|
92
|
+
hmac.digest
|
93
|
+
ensure
|
94
|
+
hmac.reset_key
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.hexdigest(key, text)
|
98
|
+
hmac = new(key)
|
99
|
+
hmac.update(text)
|
100
|
+
hmac.hexdigest
|
101
|
+
ensure
|
102
|
+
hmac.reset_key
|
103
|
+
end
|
104
|
+
|
105
|
+
private_class_method :new, :digest, :hexdigest
|
106
|
+
end
|
107
|
+
|
108
|
+
STATUS_UNDEFINED = 0
|
109
|
+
STATUS_INITIALIZED = 1
|
110
|
+
end
|
data/lib/hmac/sha1.rb
ADDED
data/lib/hmac/sha2.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "hmac/hmac"
|
2
|
+
require "digest/sha2"
|
3
|
+
|
4
|
+
module HMAC
|
5
|
+
class SHA256 < Base
|
6
|
+
def initialize(key = nil)
|
7
|
+
super(Digest::SHA256, 64, 32, key)
|
8
|
+
end
|
9
|
+
public_class_method :new, :digest, :hexdigest
|
10
|
+
end
|
11
|
+
|
12
|
+
class SHA384 < Base
|
13
|
+
def initialize(key = nil)
|
14
|
+
super(Digest::SHA384, 128, 48, key)
|
15
|
+
end
|
16
|
+
public_class_method :new, :digest, :hexdigest
|
17
|
+
end
|
18
|
+
|
19
|
+
class SHA512 < Base
|
20
|
+
def initialize(key = nil)
|
21
|
+
super(Digest::SHA512, 128, 64, key)
|
22
|
+
end
|
23
|
+
public_class_method :new, :digest, :hexdigest
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require_relative "kvform"
|
2
|
+
require_relative "util"
|
3
|
+
require_relative "cryptutil"
|
4
|
+
require_relative "message"
|
5
|
+
|
6
|
+
module OpenID
|
7
|
+
def self.get_secret_size(assoc_type)
|
8
|
+
if assoc_type == "HMAC-SHA1"
|
9
|
+
20
|
10
|
+
elsif assoc_type == "HMAC-SHA256"
|
11
|
+
32
|
12
|
+
else
|
13
|
+
raise ArgumentError("Unsupported association type: #{assoc_type}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# An Association holds the shared secret between a relying party and
|
18
|
+
# an OpenID provider.
|
19
|
+
class Association
|
20
|
+
attr_reader :handle, :secret, :issued, :lifetime, :assoc_type
|
21
|
+
|
22
|
+
FIELD_ORDER =
|
23
|
+
%i[version handle secret issued lifetime assoc_type]
|
24
|
+
|
25
|
+
# Load a serialized Association
|
26
|
+
def self.deserialize(serialized)
|
27
|
+
parsed = Util.kv_to_seq(serialized)
|
28
|
+
parsed_fields = parsed.map { |k, _v| k.to_sym }
|
29
|
+
if parsed_fields != FIELD_ORDER
|
30
|
+
raise ProtocolError, "Unexpected fields in serialized association " \
|
31
|
+
"(Expected #{FIELD_ORDER.inspect}, got #{parsed_fields.inspect})"
|
32
|
+
end
|
33
|
+
version, handle, secret64, issued_s, lifetime_s, assoc_type =
|
34
|
+
parsed.map { |_field, value| value }
|
35
|
+
if version != "2"
|
36
|
+
raise ProtocolError, "Attempted to deserialize unsupported version " \
|
37
|
+
"(#{parsed[0][1].inspect})"
|
38
|
+
end
|
39
|
+
|
40
|
+
new(
|
41
|
+
handle,
|
42
|
+
Util.from_base64(secret64),
|
43
|
+
Time.at(issued_s.to_i),
|
44
|
+
lifetime_s.to_i,
|
45
|
+
assoc_type,
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Create an Association with an issued time of now
|
50
|
+
def self.from_expires_in(expires_in, handle, secret, assoc_type)
|
51
|
+
issued = Time.now
|
52
|
+
new(handle, secret, issued, expires_in, assoc_type)
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(handle, secret, issued, lifetime, assoc_type)
|
56
|
+
@handle = handle
|
57
|
+
@secret = secret
|
58
|
+
@issued = issued
|
59
|
+
@lifetime = lifetime
|
60
|
+
@assoc_type = assoc_type
|
61
|
+
end
|
62
|
+
|
63
|
+
# Serialize the association to a form that's consistent across
|
64
|
+
# JanRain OpenID libraries.
|
65
|
+
def serialize
|
66
|
+
data = {
|
67
|
+
version: "2",
|
68
|
+
handle: handle,
|
69
|
+
secret: Util.to_base64(secret),
|
70
|
+
issued: issued.to_i.to_s,
|
71
|
+
lifetime: lifetime.to_i.to_s,
|
72
|
+
assoc_type: assoc_type,
|
73
|
+
}
|
74
|
+
|
75
|
+
Util.truthy_assert(data.length == FIELD_ORDER.length)
|
76
|
+
|
77
|
+
pairs = FIELD_ORDER.map { |field| [field.to_s, data[field]] }
|
78
|
+
Util.seq_to_kv(pairs, true)
|
79
|
+
end
|
80
|
+
|
81
|
+
# The number of seconds until this association expires
|
82
|
+
def expires_in(now = nil)
|
83
|
+
now = if now.nil?
|
84
|
+
Time.now.to_i
|
85
|
+
else
|
86
|
+
now.to_i
|
87
|
+
end
|
88
|
+
time_diff = (issued.to_i + lifetime) - now
|
89
|
+
return 0 if time_diff < 0
|
90
|
+
|
91
|
+
time_diff
|
92
|
+
end
|
93
|
+
|
94
|
+
# Generate a signature for a sequence of [key, value] pairs
|
95
|
+
def sign(pairs)
|
96
|
+
kv = Util.seq_to_kv(pairs)
|
97
|
+
case assoc_type
|
98
|
+
when "HMAC-SHA1"
|
99
|
+
CryptUtil.hmac_sha1(@secret, kv)
|
100
|
+
when "HMAC-SHA256"
|
101
|
+
CryptUtil.hmac_sha256(@secret, kv)
|
102
|
+
else
|
103
|
+
raise ProtocolError, "Association has unknown type: " \
|
104
|
+
"#{assoc_type.inspect}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Generate the list of pairs that form the signed elements of the
|
109
|
+
# given message
|
110
|
+
def make_pairs(message)
|
111
|
+
signed = message.get_arg(OPENID_NS, "signed")
|
112
|
+
raise ProtocolError, "Missing signed list" if signed.nil?
|
113
|
+
|
114
|
+
signed_fields = signed.split(",", -1)
|
115
|
+
data = message.to_post_args
|
116
|
+
signed_fields.map { |field| [field, data.fetch("openid." + field, "")] }
|
117
|
+
end
|
118
|
+
|
119
|
+
# Return whether the message's signature passes
|
120
|
+
def check_message_signature(message)
|
121
|
+
message_sig = message.get_arg(OPENID_NS, "sig")
|
122
|
+
raise ProtocolError, "#{message} has no sig." if message_sig.nil?
|
123
|
+
|
124
|
+
calculated_sig = get_message_signature(message)
|
125
|
+
CryptUtil.const_eq(calculated_sig, message_sig)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Get the signature for this message
|
129
|
+
def get_message_signature(message)
|
130
|
+
Util.to_base64(sign(make_pairs(message)))
|
131
|
+
end
|
132
|
+
|
133
|
+
def ==(other)
|
134
|
+
(other.class == self.class and
|
135
|
+
other.handle == handle and
|
136
|
+
other.secret == secret and
|
137
|
+
|
138
|
+
# The internals of the time objects seemed to differ
|
139
|
+
# in an opaque way when serializing/unserializing.
|
140
|
+
# I don't think this will be a problem.
|
141
|
+
other.issued.to_i == issued.to_i and
|
142
|
+
|
143
|
+
other.lifetime == lifetime and
|
144
|
+
other.assoc_type == assoc_type)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Add a signature (and a signed list) to a message.
|
148
|
+
def sign_message(message)
|
149
|
+
if message.has_key?(OPENID_NS, "sig") or
|
150
|
+
message.has_key?(OPENID_NS, "signed")
|
151
|
+
raise ArgumentError, "Message already has signed list or signature"
|
152
|
+
end
|
153
|
+
|
154
|
+
extant_handle = message.get_arg(OPENID_NS, "assoc_handle")
|
155
|
+
raise ArgumentError, "Message has a different association handle" if extant_handle and extant_handle != handle
|
156
|
+
|
157
|
+
signed_message = message.copy
|
158
|
+
signed_message.set_arg(OPENID_NS, "assoc_handle", handle)
|
159
|
+
message_keys = signed_message.to_post_args.keys
|
160
|
+
|
161
|
+
signed_list = []
|
162
|
+
message_keys.each do |k|
|
163
|
+
signed_list << k[7..-1] if k.start_with?("openid.")
|
164
|
+
end
|
165
|
+
|
166
|
+
signed_list << "signed"
|
167
|
+
signed_list.sort!
|
168
|
+
|
169
|
+
signed_message.set_arg(OPENID_NS, "signed", signed_list.join(","))
|
170
|
+
sig = get_message_signature(signed_message)
|
171
|
+
signed_message.set_arg(OPENID_NS, "sig", sig)
|
172
|
+
signed_message
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
class AssociationNegotiator
|
177
|
+
attr_reader :allowed_types
|
178
|
+
|
179
|
+
def self.get_session_types(assoc_type)
|
180
|
+
case assoc_type
|
181
|
+
when "HMAC-SHA1"
|
182
|
+
%w[DH-SHA1 no-encryption]
|
183
|
+
when "HMAC-SHA256"
|
184
|
+
%w[DH-SHA256 no-encryption]
|
185
|
+
else
|
186
|
+
raise ProtocolError, "Unknown association type #{assoc_type.inspect}"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.check_session_type(assoc_type, session_type)
|
191
|
+
return if get_session_types(assoc_type).include?(session_type)
|
192
|
+
|
193
|
+
raise ProtocolError, "Session type #{session_type.inspect} not " \
|
194
|
+
"valid for association type #{assoc_type.inspect}"
|
195
|
+
end
|
196
|
+
|
197
|
+
def initialize(allowed_types)
|
198
|
+
self.allowed_types = (allowed_types)
|
199
|
+
end
|
200
|
+
|
201
|
+
def copy
|
202
|
+
Marshal.load(Marshal.dump(self))
|
203
|
+
end
|
204
|
+
|
205
|
+
def allowed_types=(allowed_types)
|
206
|
+
allowed_types.each do |assoc_type, session_type|
|
207
|
+
self.class.check_session_type(assoc_type, session_type)
|
208
|
+
end
|
209
|
+
@allowed_types = allowed_types
|
210
|
+
end
|
211
|
+
|
212
|
+
def add_allowed_type(assoc_type, session_type = nil)
|
213
|
+
if session_type.nil?
|
214
|
+
session_types = self.class.get_session_types(assoc_type)
|
215
|
+
else
|
216
|
+
self.class.check_session_type(assoc_type, session_type)
|
217
|
+
session_types = [session_type]
|
218
|
+
end
|
219
|
+
for session_type in session_types do
|
220
|
+
@allowed_types << [assoc_type, session_type]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def allowed?(assoc_type, session_type)
|
225
|
+
@allowed_types.include?([assoc_type, session_type])
|
226
|
+
end
|
227
|
+
|
228
|
+
def get_allowed_type
|
229
|
+
@allowed_types.empty? ? nil : @allowed_types[0]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
DefaultNegotiator =
|
234
|
+
AssociationNegotiator.new([
|
235
|
+
%w[HMAC-SHA1 DH-SHA1],
|
236
|
+
%w[HMAC-SHA1 no-encryption],
|
237
|
+
%w[HMAC-SHA256 DH-SHA256],
|
238
|
+
%w[HMAC-SHA256 no-encryption],
|
239
|
+
])
|
240
|
+
|
241
|
+
EncryptedNegotiator =
|
242
|
+
AssociationNegotiator.new([
|
243
|
+
%w[HMAC-SHA1 DH-SHA1],
|
244
|
+
%w[HMAC-SHA256 DH-SHA256],
|
245
|
+
])
|
246
|
+
end
|