siwe 1.1.2 → 2.0.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 +4 -4
- data/.rubocop.yml +7 -1
- data/Gemfile.lock +2 -2
- data/lib/siwe/exceptions.rb +57 -1
- data/lib/siwe/message.rb +98 -57
- data/lib/siwe/version.rb +1 -1
- data/lib/siwe.rb +9 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 933ba5a3c973570550d45cd839212a57c6a66568e17a2b107be945fc5ee1c6f3
|
4
|
+
data.tar.gz: 6ea0957b133e576102f9038cee3a7c4459cba2d7bbbe11d4268ac8d464e5982b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ecf2bac358261ae3cd49f8c5ef43ddc7724360705dddaf2704cf3ba427bbb2181bc20a5189cc66bd991144cc669a8fb9555db36780f439dfa6b0d007c941202
|
7
|
+
data.tar.gz: d63ad437299d1386f5cfce1ce44a4d76ecd7a8bcffd3e819f3d2b625196a3b4ced5128544a1413c76447b918e901fa78b63ed4268b6785c631ba18e3675c5327
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
siwe (
|
4
|
+
siwe (2.0.0)
|
5
5
|
eth (~> 0.5.1)
|
6
6
|
|
7
7
|
GEM
|
@@ -12,7 +12,7 @@ GEM
|
|
12
12
|
benchmark (0.2.0)
|
13
13
|
diff-lcs (1.5.0)
|
14
14
|
e2mmap (0.1.0)
|
15
|
-
eth (0.5.
|
15
|
+
eth (0.5.2)
|
16
16
|
keccak (~> 1.3)
|
17
17
|
konstructor (~> 1.0)
|
18
18
|
openssl (~> 2.2)
|
data/lib/siwe/exceptions.rb
CHANGED
@@ -3,7 +3,28 @@
|
|
3
3
|
module Siwe
|
4
4
|
# Used when the message is already expired. (Expires At < Time.now)
|
5
5
|
class ExpiredMessage < StandardError
|
6
|
-
def initialize(msg = "
|
6
|
+
def initialize(msg = "Expired message.")
|
7
|
+
super
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Used when the domain is not a valid authority or is empty.
|
12
|
+
class InvalidDomain < StandardError
|
13
|
+
def initialize(msg = "Invalid domain.")
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Used when the domain doesn't match the domain provided for verification.
|
19
|
+
class DomainMismatch < StandardError
|
20
|
+
def initialize(msg = "Domain does not match provided domain for verification.")
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Used when the nonce doesn't match the nonce provided for verification.
|
26
|
+
class NonceMismatch < StandardError
|
27
|
+
def initialize(msg = "Nonce does not match provided nonce for verification.")
|
7
28
|
super
|
8
29
|
end
|
9
30
|
end
|
@@ -15,6 +36,20 @@ module Siwe
|
|
15
36
|
end
|
16
37
|
end
|
17
38
|
|
39
|
+
# Used when the message is created with an invalid URI
|
40
|
+
class InvalidURI < StandardError
|
41
|
+
def initialize(msg = "URI does not conform to RFC 3986.")
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Used when the nonce is smaller then 8 characters or is not alphanumeric
|
47
|
+
class InvalidNonce < StandardError
|
48
|
+
def initialize(msg = "Nonce size smaller then 8 characters or is not alphanumeric.")
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
18
53
|
# Used when the message is not yet valid. (Not Before > Time.now)
|
19
54
|
class NotValidMessage < StandardError
|
20
55
|
def initialize(msg = "Message not yet valid.")
|
@@ -22,10 +57,31 @@ module Siwe
|
|
22
57
|
end
|
23
58
|
end
|
24
59
|
|
60
|
+
# Used when the message contains a time format not compliant to ISO8601.
|
61
|
+
class InvalidTimeFormat < StandardError
|
62
|
+
def initialize(field, msg = "Invalid time format for: #{field}")
|
63
|
+
super
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Used when the message version is not 1.
|
68
|
+
class InvalidMessageVersion < StandardError
|
69
|
+
def initialize(msg = "Invalid message version.")
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
25
74
|
# Used when the signature doesn't correspond to the address of the message.
|
26
75
|
class InvalidSignature < StandardError
|
27
76
|
def initialize(msg = "Signature doesn't match message.")
|
28
77
|
super
|
29
78
|
end
|
30
79
|
end
|
80
|
+
|
81
|
+
# Used when the message doesn't match the RegExp.
|
82
|
+
class UnableToParseMessage < StandardError
|
83
|
+
def initialize(msg = "Unable to parse message.")
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
31
87
|
end
|
data/lib/siwe/message.rb
CHANGED
@@ -4,26 +4,27 @@ require "time"
|
|
4
4
|
require "eth"
|
5
5
|
require "json"
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
7
|
+
DOMAIN = %r{(?<domain>[^/?#]+)}.freeze
|
8
|
+
SIWE_DOMAIN = %r{^#{DOMAIN.source} wants you to sign in with your Ethereum account:}.freeze
|
9
|
+
|
10
|
+
SIWE_ADDRESS = %r{\n(?<address>0x[a-zA-Z0-9]{40})\n\n}.freeze
|
11
|
+
SIWE_STATEMENT = %r{((?<statement>[^\n]+)\n)?}.freeze
|
12
|
+
RFC3986 = %r{(([^:?#]+):)?(([^?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?}.freeze
|
13
|
+
SIWE_URI_LINE = %r{\nURI: (?<uri>#{RFC3986.source}?)}.freeze
|
14
|
+
SIWE_VERSION = %r{\nVersion: (?<version>1)}.freeze
|
15
|
+
SIWE_CHAIN_ID = %r{\nChain ID: (?<chain_id>[0-9]+)}.freeze
|
16
|
+
SIWE_NONCE = %r{\nNonce: (?<nonce>[a-zA-Z0-9]{8,})}.freeze
|
17
|
+
SIWE_DATETIME = %r{([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))}.freeze
|
18
|
+
SIWE_ISSUED_AT = %r{\nIssued At: (?<issued_at>#{SIWE_DATETIME.source})}.freeze
|
19
|
+
SIWE_EXPIRATION_TIME = %r{(\nExpiration Time: (?<expiration_time>#{SIWE_DATETIME.source}))?}.freeze
|
20
|
+
SIWE_NOT_BEFORE = %r{(\nNot Before: (?<not_before>#{SIWE_DATETIME.source}))?}.freeze
|
21
|
+
SIWE_REQUEST_ID = %r{(\nRequest ID: (?<request_id>[-._~!$&'()*+,;=:@%a-zA-Z0-9]*))?}.freeze
|
22
|
+
SIWE_RESOURCES = %r{(\nResources:(?<resources>(\n- #{RFC3986.source}?)+))?$}.freeze
|
23
|
+
|
24
|
+
SIWE_MESSAGE = Regexp.new(SIWE_DOMAIN.source + SIWE_ADDRESS.source + SIWE_STATEMENT.source + SIWE_URI_LINE.source +
|
25
|
+
SIWE_VERSION.source + SIWE_CHAIN_ID.source + SIWE_NONCE.source + SIWE_ISSUED_AT.source +
|
26
|
+
SIWE_EXPIRATION_TIME.source + SIWE_NOT_BEFORE.source + SIWE_REQUEST_ID.source +
|
27
|
+
SIWE_RESOURCES.source)
|
27
28
|
module Siwe
|
28
29
|
# Class that defines the EIP-4361 message fields and some utility methods to
|
29
30
|
# generate/validate the messages
|
@@ -76,47 +77,41 @@ module Siwe
|
|
76
77
|
|
77
78
|
def initialize(domain, address, uri, version, options = {})
|
78
79
|
@domain = domain
|
79
|
-
|
80
|
-
@address = Eth::Address.new(address).to_s
|
81
|
-
rescue StandardError
|
82
|
-
raise Siwe::InvalidAddress
|
83
|
-
end
|
84
|
-
raise Siwe::InvalidAddress unless @address.eql? address
|
85
|
-
|
80
|
+
@address = address
|
86
81
|
@uri = uri
|
87
82
|
@version = version
|
88
83
|
@statement = options.fetch :statement, ""
|
89
84
|
@issued_at = options.fetch :issued_at, Time.now.utc.iso8601
|
90
85
|
@nonce = options.fetch :nonce, Siwe::Util.generate_nonce
|
91
|
-
@chain_id = options.fetch :chain_id,
|
86
|
+
@chain_id = options.fetch :chain_id, 1
|
92
87
|
@expiration_time = options.fetch :expiration_time, ""
|
93
88
|
@not_before = options.fetch :not_before, ""
|
94
89
|
@request_id = options.fetch :request_id, ""
|
95
90
|
@resources = options.fetch :resources, []
|
91
|
+
validate
|
96
92
|
end
|
97
93
|
|
98
94
|
def self.from_message(msg)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
95
|
+
message = msg.match SIWE_MESSAGE
|
96
|
+
|
97
|
+
raise Siwe::UnableToParseMessage unless message.to_s == msg
|
98
|
+
|
99
|
+
new(
|
100
|
+
message[:domain],
|
101
|
+
message[:address],
|
102
|
+
message[:uri],
|
103
|
+
message[:version],
|
104
|
+
{
|
105
|
+
statement: message[:statement],
|
106
|
+
issued_at: message[:issued_at],
|
107
|
+
nonce: message[:nonce],
|
108
|
+
chain_id: message[:chain_id].to_i,
|
109
|
+
expiration_time: message[:expiration_time],
|
110
|
+
not_before: message[:not_before],
|
111
|
+
request_id: message[:request_id],
|
112
|
+
resources: message[:resources]&.split("\n- ")&.drop(1)
|
113
|
+
}
|
114
|
+
)
|
120
115
|
end
|
121
116
|
|
122
117
|
def to_json_string
|
@@ -156,15 +151,62 @@ module Siwe
|
|
156
151
|
)
|
157
152
|
end
|
158
153
|
|
159
|
-
def validate
|
160
|
-
|
161
|
-
raise Siwe::
|
154
|
+
def validate
|
155
|
+
# check domain
|
156
|
+
raise Siwe::InvalidDomain unless @domain.match %r{[^/?#]*} || @domain.empty?
|
157
|
+
|
158
|
+
# check address EIP-55
|
159
|
+
raise Siwe::InvalidAddress unless Eth::Address.new(@address).to_s.eql? @address
|
160
|
+
|
161
|
+
# check uri
|
162
|
+
raise Siwe::InvalidURI unless URI.parse(@uri)
|
163
|
+
|
164
|
+
# check version
|
165
|
+
raise Siwe::InvalidMessageVersion unless @version == "1"
|
166
|
+
|
167
|
+
# check if the nonce is alphanumeric and bigger then 8 characters
|
168
|
+
raise Siwe::InvalidNonce unless @nonce.match(%r{[a-zA-Z0-9]{8,}})
|
169
|
+
|
170
|
+
# check issued_at format
|
171
|
+
begin
|
172
|
+
Time.iso8601(@issued_at)
|
173
|
+
rescue ArgumentError
|
174
|
+
raise Siwe::InvalidTimeFormat, "issued_at"
|
175
|
+
end
|
176
|
+
|
177
|
+
# check exp_time
|
178
|
+
begin
|
179
|
+
Time.iso8601(@expiration_time) unless @expiration_time.nil? || @expiration_time.empty?
|
180
|
+
rescue ArgumentError
|
181
|
+
raise Siwe::InvalidTimeFormat, "expiration_time"
|
182
|
+
end
|
183
|
+
|
184
|
+
# check not_before
|
185
|
+
begin
|
186
|
+
Time.iso8601(@not_before) unless @not_before.nil? || @not_before.empty?
|
187
|
+
rescue ArgumentError
|
188
|
+
raise Siwe::InvalidTimeFormat, "not_before"
|
189
|
+
end
|
190
|
+
|
191
|
+
# check resources
|
192
|
+
raise Siwe::InvalidURI unless @resources.nil? || @resources.empty? || @resources.each { |uri| URI.parse(uri) }
|
193
|
+
end
|
194
|
+
|
195
|
+
def verify(signature, domain, time, nonce)
|
196
|
+
raise Siwe::DomainMismatch unless domain.nil? || domain.eql?(@domain)
|
197
|
+
|
198
|
+
raise Siwe::NonceMismatch unless nonce.nil? || nonce.eql?(@nonce)
|
199
|
+
|
200
|
+
check_time = time.nil? ? Time.now.utc : Time.iso8601(time)
|
201
|
+
|
202
|
+
raise Siwe::ExpiredMessage if (!@expiration_time.nil? && !@expiration_time.empty?) && check_time > Time.iso8601(@expiration_time)
|
203
|
+
|
204
|
+
raise Siwe::NotValidMessage if (!@not_before.nil? && !@not_before.empty?) && check_time < Time.iso8601(@not_before)
|
162
205
|
|
163
|
-
raise Siwe::InvalidSignature if signature.empty?
|
206
|
+
raise Siwe::InvalidSignature if signature.nil? && signature.empty?
|
164
207
|
|
165
208
|
raise Siwe::InvalidAddress unless @address.eql?(Eth::Address.new(@address).to_s)
|
166
209
|
|
167
|
-
puts "whatever"
|
168
210
|
begin
|
169
211
|
pub_key = Eth::Signature.personal_recover prepare_message, signature
|
170
212
|
signature_address = Eth::Util.public_key_to_address pub_key
|
@@ -184,7 +226,7 @@ module Siwe
|
|
184
226
|
|
185
227
|
header = [greeting, address]
|
186
228
|
|
187
|
-
if @statement.empty?
|
229
|
+
if @statement.nil? || @statement.empty?
|
188
230
|
header.push "\n"
|
189
231
|
else
|
190
232
|
header.push statement
|
@@ -203,7 +245,6 @@ module Siwe
|
|
203
245
|
expiration_time = "Expiration Time: #{@expiration_time}"
|
204
246
|
not_before = "Not Before: #{@not_before}"
|
205
247
|
request_id = "Request ID: #{@request_id}"
|
206
|
-
resources = "Resources:\n#{@resources.map { |x| "- #{x}" }.join "\n"}"
|
207
248
|
|
208
249
|
body.push expiration_time unless @expiration_time.to_s.strip.empty?
|
209
250
|
|
@@ -211,7 +252,7 @@ module Siwe
|
|
211
252
|
|
212
253
|
body.push request_id unless @request_id.to_s.strip.empty?
|
213
254
|
|
214
|
-
body.push resources unless @resources.empty?
|
255
|
+
body.push "Resources:\n#{@resources.map { |x| "- #{x}" }.join "\n"}" unless @resources.nil? || @resources.empty?
|
215
256
|
|
216
257
|
body = body.join "\n"
|
217
258
|
|
data/lib/siwe/version.rb
CHANGED
data/lib/siwe.rb
CHANGED
@@ -7,9 +7,17 @@ module Siwe
|
|
7
7
|
autoload :Message, "siwe/message"
|
8
8
|
autoload :Util, "siwe/util"
|
9
9
|
autoload :ExpiredMessage, "siwe/exceptions"
|
10
|
+
autoload :InvalidDomain, "siwe/exceptions"
|
11
|
+
autoload :DomainMismatch, "siwe/exceptions"
|
12
|
+
autoload :NonceMismatch, "siwe/exceptions"
|
13
|
+
autoload :InvalidAddress, "siwe/exceptions"
|
14
|
+
autoload :InvalidURI, "siwe/exceptions"
|
15
|
+
autoload :InvalidNonce, "siwe/exceptions"
|
10
16
|
autoload :NotValidMessage, "siwe/exceptions"
|
17
|
+
autoload :InvalidTimeFormat, "siwe/exceptions"
|
18
|
+
autoload :InvalidMessageVersion, "siwe/exceptions"
|
11
19
|
autoload :InvalidSignature, "siwe/exceptions"
|
12
|
-
autoload :
|
20
|
+
autoload :UnableToParseMessage, "siwe/exceptions"
|
13
21
|
|
14
22
|
class Error < StandardError; end
|
15
23
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: siwe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Spruce Systems Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eth
|