sirp 2.0.0.pre → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +4 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/README.md +170 -36
- data/lib/sirp.rb +3 -1
- data/lib/sirp/client.rb +67 -21
- data/lib/sirp/parameters.rb +171 -0
- data/lib/sirp/sirp.rb +141 -261
- data/lib/sirp/verifier.rb +87 -36
- data/lib/sirp/version.rb +1 -1
- data/sirp.gemspec +4 -3
- metadata +32 -24
- metadata.gz.sig +0 -0
- data/examples/Gemfile +0 -6
- data/examples/README.md +0 -34
- data/examples/clients/javascript/.gitignore +0 -1
- data/examples/clients/javascript/app.js +0 -59
- data/examples/clients/javascript/index.html +0 -23
- data/examples/clients/javascript/package.json +0 -15
- data/examples/clients/ruby/client.rb +0 -48
- data/examples/server.rb +0 -88
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c52dff2045e6a63899115731fa0b6b47c211e83
|
4
|
+
data.tar.gz: b39d228ae76c0afb76e228203025ef9c3ebfd33d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e13201e91458fd8f6d69e70482038a37007bd8faff54ca83c2b4ffa87be1f4906a49480ef3256152dde8b18fd1955566dbfc4b733ad1257fe159077aa8456f06
|
7
|
+
data.tar.gz: 2f129346aa66933aecbdb7ed76d26e32ac67a423a03ae291e3d995c9e8c1de6a3786bfa1c7975556f3d0d648da5097a88635aa2eff8dae544df1c2c121a58fe0
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private lib/**/*.rb - README.md LICENSE.txt CODE_OF_CONDUCT.md
|
data/CHANGELOG.md
CHANGED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at glenn@rempe.us. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/README.md
CHANGED
@@ -9,29 +9,47 @@
|
|
9
9
|
|
10
10
|
Ruby Docs : [http://www.rubydoc.info/gems/sirp](http://www.rubydoc.info/gems/sirp)
|
11
11
|
|
12
|
-
|
13
12
|
This is a pure Ruby implementation of the
|
14
13
|
[Secure Remote Password](http://srp.stanford.edu/) protocol (SRP-6a),
|
15
14
|
which is a 'zero-knowledge' mutual authentication system.
|
16
15
|
|
17
|
-
|
18
|
-
over an insecure network connection without revealing the password
|
19
|
-
client lacks the user's password or the server
|
20
|
-
key, the authentication will fail. This approach
|
21
|
-
vast majority of authentication systems in
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
16
|
+
SRP is an protocol that allows for mutual authentication of a client and
|
17
|
+
server over an insecure network connection without revealing the password to the
|
18
|
+
server or an eavesdropper. If the client lacks the user's password, or the server
|
19
|
+
lacks the proper verification key, the authentication will fail. This approach
|
20
|
+
is much more secure than the vast majority of authentication systems in common use
|
21
|
+
since the password is never sent over the wire. The password is impossible to
|
22
|
+
intercept, or to be revealed in a server breach, unless the verifier can be
|
23
|
+
reversed. Since the verifier is derived from the password + salt through
|
24
|
+
cryptographic one-way hash functions and Modular Exponentiation. Attacking the
|
25
|
+
verifier to retrieve a password would be of similar difficulty
|
26
|
+
as deriving a private encryption key from its public key. Extremely difficult, if
|
27
|
+
not impossible.
|
26
28
|
|
27
29
|
Unlike other common challenge-response authentication protocols, such as
|
28
|
-
Kerberos and SSL,
|
30
|
+
Kerberos and SSL, SRP does not rely on an external infrastructure of trusted
|
29
31
|
key servers or complex certificate management.
|
30
32
|
|
33
|
+
At the end of the authentication process both the client and the server will have
|
34
|
+
negotiated a shared strong encryption key suitable for encrypted session
|
35
|
+
communications. This key is negotiated through a modified Diffie-Hellman
|
36
|
+
key exchange and the key is never sent over the wire.
|
37
|
+
|
38
|
+
SiRP is designed to be interoperable with a Ruby client and server, or
|
39
|
+
with Ruby on the server side, and the [JSRP](https://github.com/alax/jsrp)
|
40
|
+
Javascript client running in a browser.
|
41
|
+
|
42
|
+
## Live Demo
|
43
|
+
|
44
|
+
You can try out an interactive demo at
|
45
|
+
[https://sirp-demo.herokuapp.com/index.html](https://sirp-demo.herokuapp.com/index.html).
|
46
|
+
|
47
|
+
[Demo Source Code @ grempe/sirp-demo](https://github.com/grempe/sirp-demo)
|
48
|
+
|
31
49
|
## Documentation
|
32
50
|
|
33
51
|
There is pretty extensive inline documentation. You can view the latest
|
34
|
-
|
52
|
+
API docs at [http://www.rubydoc.info/gems/sirp](http://www.rubydoc.info/gems/sirp)
|
35
53
|
|
36
54
|
You can check my documentation quality score at
|
37
55
|
[http://inch-ci.org/github/grempe/sirp](http://inch-ci.org/github/grempe/sirp?branch=master)
|
@@ -40,9 +58,11 @@ You can check my documentation quality score at
|
|
40
58
|
|
41
59
|
SiRP is continuously integration tested on the following Ruby VMs:
|
42
60
|
|
43
|
-
* MRI 2.1
|
61
|
+
* MRI 2.1
|
62
|
+
* MRI 2.2
|
63
|
+
* MRI 2.3
|
44
64
|
|
45
|
-
|
65
|
+
Ruby versions < 2.1 are not supported.
|
46
66
|
|
47
67
|
## Installation
|
48
68
|
|
@@ -119,11 +139,83 @@ compliant third-party libraries:
|
|
119
139
|
|
120
140
|
[JSRP / JavaScript](https://github.com/alax/jsrp)
|
121
141
|
|
142
|
+
## SRP-6a Protocol Design
|
143
|
+
|
144
|
+
Extracted from [http://srp.stanford.edu/design.html](http://srp.stanford.edu/design.html)
|
145
|
+
|
146
|
+
```
|
147
|
+
SRP is the newest addition to a new class of strong authentication protocols
|
148
|
+
that resist all the well-known passive and active attacks over the network.
|
149
|
+
SRP borrows some elements from other key-exchange and identification protcols
|
150
|
+
and adds some subtle modifications and refinements. The result is a protocol
|
151
|
+
that preserves the strength and efficiency of the EKE family protocols while
|
152
|
+
fixing some of their shortcomings.
|
153
|
+
|
154
|
+
The following is a description of SRP-6 and 6a, the latest versions of SRP:
|
155
|
+
|
156
|
+
N A large safe prime (N = 2q+1, where q is prime)
|
157
|
+
All arithmetic is done modulo N.
|
158
|
+
g A generator modulo N
|
159
|
+
k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
|
160
|
+
s User's salt
|
161
|
+
I Username
|
162
|
+
p Cleartext Password
|
163
|
+
H() One-way hash function
|
164
|
+
^ (Modular) Exponentiation
|
165
|
+
u Random scrambling parameter
|
166
|
+
a,b Secret ephemeral values
|
167
|
+
A,B Public ephemeral values
|
168
|
+
x Private key (derived from p and s)
|
169
|
+
v Password verifier
|
170
|
+
|
171
|
+
The host stores passwords using the following formula:
|
172
|
+
|
173
|
+
x = H(s, p) (s is chosen randomly)
|
174
|
+
v = g^x (computes password verifier)
|
175
|
+
|
176
|
+
The host then keeps {I, s, v} in its password database. The authentication
|
177
|
+
protocol itself goes as follows:
|
178
|
+
|
179
|
+
User -> Host: I, A = g^a (identifies self, a = random number)
|
180
|
+
Host -> User: s, B = kv + g^b (sends salt, b = random number)
|
181
|
+
|
182
|
+
Both: u = H(A, B)
|
183
|
+
|
184
|
+
User: x = H(s, p) (user enters password)
|
185
|
+
User: S = (B - kg^x) ^ (a + ux) (computes session key)
|
186
|
+
User: K = H(S)
|
187
|
+
|
188
|
+
Host: S = (Av^u) ^ b (computes session key)
|
189
|
+
Host: K = H(S)
|
190
|
+
|
191
|
+
Now the two parties have a shared, strong session key K. To complete
|
192
|
+
authentication, they need to prove to each other that their keys match.
|
193
|
+
One possible way:
|
194
|
+
|
195
|
+
User -> Host: M = H(H(N) xor H(g), H(I), s, A, B, K)
|
196
|
+
Host -> User: H(A, M, K)
|
197
|
+
|
198
|
+
The two parties also employ the following safeguards:
|
199
|
+
|
200
|
+
* The user will abort if he receives B == 0 (mod N) or u == 0.
|
201
|
+
* The host will abort if it detects that A == 0 (mod N).
|
202
|
+
* The user must show his proof of K first. If the server detects that the
|
203
|
+
user's proof is incorrect, it must abort without showing its own proof of K.
|
204
|
+
```
|
205
|
+
|
122
206
|
## Usage Example
|
123
207
|
|
124
208
|
In this example the client and server steps are interleaved for demonstration
|
125
|
-
purposes. See the
|
126
|
-
|
209
|
+
purposes. See the [grempe/sirp-demo](https://github.com/grempe/sirp-demo)
|
210
|
+
repository for working sample code and a live demo. The phases of authentication
|
211
|
+
in this example are delineated by the HTTPS request/response between client and
|
212
|
+
server. The concept of 'phases' is something noted here for convenience. The
|
213
|
+
specification makes no mention of phases since it is implementation specific.
|
214
|
+
|
215
|
+
This example is useful for showing the ordering and arguments in the public
|
216
|
+
API and is not intended to be a 'copy & paste' code sample since the
|
217
|
+
client and server interaction is something left up to the implementer and likely
|
218
|
+
different in every case.
|
127
219
|
|
128
220
|
``` ruby
|
129
221
|
require 'sirp'
|
@@ -132,22 +224,42 @@ username = 'user'
|
|
132
224
|
password = 'password'
|
133
225
|
prime_length = 2048
|
134
226
|
|
135
|
-
#
|
227
|
+
# ~~~ Phase 0 : User Registration ~~~
|
228
|
+
|
229
|
+
# One time only! SRP is a form of TOFU (Trust On First Use) authentication
|
230
|
+
# where all is predicated on the client being able to register a verifier
|
231
|
+
# with the server upon initial registration. The server promises in turn to
|
232
|
+
# keep this verifier secret and never reveal it. If this first interaction
|
233
|
+
# is compromised then all is lost. If the verifier is revealed then there
|
234
|
+
# is a theoretical attack on the verifier which could reveal information
|
235
|
+
# about the password. It is likely cryptographically difficult though.
|
236
|
+
# It is important that the username and password combination be of
|
237
|
+
# high entropy.
|
238
|
+
|
239
|
+
# The salt and verifier should be persisted server-side, accessible by
|
240
|
+
# looking up via the username. The server must protect the verifier but
|
241
|
+
# will return the salt to any party requesting authentication who knows
|
242
|
+
# the username.
|
243
|
+
|
136
244
|
@auth = SIRP::Verifier.new(prime_length).generate_userauth(username, password)
|
137
|
-
#
|
245
|
+
# => {username: '...', verifier: '...', salt: '...'}
|
138
246
|
|
139
|
-
# ~~~
|
247
|
+
# ~~~ Phase 1 : Challenge/Response ~~~
|
140
248
|
|
141
249
|
client = SIRP::Client.new(prime_length)
|
142
250
|
A = client.start_authentication
|
143
251
|
|
144
|
-
# Client => Server: username
|
252
|
+
# HTTPS POST Client => Server: request includes 'username' and 'A'
|
145
253
|
|
146
|
-
# Server retrieves user's verifier and salt from the database
|
254
|
+
# Server retrieves user's verifier and salt from the database by
|
255
|
+
# looking up these values indexed by 'username'. Here simulated
|
256
|
+
# by using the @auth hash directly.
|
147
257
|
v = @auth[:verifier]
|
148
258
|
salt = @auth[:salt]
|
149
259
|
|
150
|
-
# Server generates challenge for the client
|
260
|
+
# Server generates a challenge for the client and a proof it will require
|
261
|
+
# in Phase 2 of the auth process. The challenge is given to the client, the
|
262
|
+
# proof is temporarily persisted.
|
151
263
|
verifier = SIRP::Verifier.new(prime_length)
|
152
264
|
session = verifier.get_challenge_and_proof(username, v, salt, A)
|
153
265
|
|
@@ -157,29 +269,49 @@ session = verifier.get_challenge_and_proof(username, v, salt, A)
|
|
157
269
|
# Server sends the challenge containing salt and B to client.
|
158
270
|
response = session[:challenge]
|
159
271
|
|
160
|
-
# Server => Client: salt, B
|
272
|
+
# HTTPS Server => Client: response includes 'salt', and 'B'
|
161
273
|
|
162
|
-
#
|
274
|
+
# ~~~ Phase 2 : Continue Authentication ~~~
|
275
|
+
|
276
|
+
# Client calculates M as a response to the challenge using the
|
277
|
+
# username and password and the server provided 'salt' and 'B'.
|
163
278
|
client_M = client.process_challenge(username, password, salt, B)
|
164
279
|
|
165
|
-
# Client => Server: username, M
|
280
|
+
# HTTPS POST Client => Server: request includes 'username', and 'M'
|
166
281
|
|
167
282
|
# Instantiate a new verifier on the server.
|
168
283
|
verifier = SIRP::Verifier.new(prime_length)
|
169
284
|
|
170
|
-
# Verify challenge response M.
|
171
|
-
#
|
285
|
+
# Verify challenge response M against the Verifier proof stored earlier.
|
286
|
+
# server_H_AMK returned will be 'false' if verification failed.
|
172
287
|
server_H_AMK = verifier.verify_session(@proof, client_M)
|
173
|
-
# Is false if authentication failed.
|
174
|
-
|
175
|
-
# At this point, the client and server should have a common session key
|
176
|
-
# that is secure (i.e. not known to an outside party). To finish
|
177
|
-
# authentication, they must prove to each other that their keys are
|
178
|
-
# identical.
|
179
288
|
|
180
|
-
#
|
181
|
-
|
182
|
-
|
289
|
+
# At this point, the client and server should have a common session key (K)
|
290
|
+
# that is secure and unknown to any outside party. Before they can safely use
|
291
|
+
# it though they must prove to each other that their keys are identical by
|
292
|
+
# exchanging a hash H(A,M,K). This step allows both client and server to be
|
293
|
+
# certain they arrived at the same values independently.
|
294
|
+
|
295
|
+
# The server sends a response based on the results of verify_session.
|
296
|
+
|
297
|
+
if server_H_AMK
|
298
|
+
# HTTPS Server => Client: response includes server_H_AMK
|
299
|
+
else
|
300
|
+
# Do NOT include the server_H_AMK in the response.
|
301
|
+
# HTTPS Server => Client: 401 Unauthorized
|
302
|
+
end
|
303
|
+
|
304
|
+
# The client compares server_H_AMK response to its own calculated H(A,M,K).
|
305
|
+
|
306
|
+
if client.verify(server_H_AMK)
|
307
|
+
#### SUCCESS ####
|
308
|
+
# Client and server have mutually authenticated.
|
309
|
+
# Optional : Use this secret key to derive shared encryption keys for some
|
310
|
+
# other application specific use.
|
311
|
+
secret_key = client.K
|
312
|
+
else
|
313
|
+
# FAIL, THROW IT ALL AWAY AND START OVER!
|
314
|
+
end
|
183
315
|
|
184
316
|
```
|
185
317
|
|
@@ -200,6 +332,8 @@ interactive prompt that will allow you to experiment.
|
|
200
332
|
|
201
333
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
202
334
|
|
335
|
+
The formal release process can be found in [RELEASE.md](https://github.com/grempe/sirp/blob/master/RELEASE.md)
|
336
|
+
|
203
337
|
### Contributing
|
204
338
|
|
205
339
|
Bug reports and pull requests are welcome on GitHub
|
data/lib/sirp.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
require 'digest'
|
3
3
|
require 'rbnacl/libsodium'
|
4
|
-
require '
|
4
|
+
require 'sysrandom/securerandom'
|
5
|
+
require 'hashie'
|
5
6
|
require 'sirp/sirp'
|
7
|
+
require 'sirp/parameters'
|
6
8
|
require 'sirp/client'
|
7
9
|
require 'sirp/verifier'
|
8
10
|
require 'sirp/version'
|
data/lib/sirp/client.rb
CHANGED
@@ -1,50 +1,96 @@
|
|
1
1
|
module SIRP
|
2
2
|
class Client
|
3
|
+
include SIRP
|
3
4
|
attr_reader :N, :g, :k, :a, :A, :S, :K, :M, :H_AMK, :hash
|
4
5
|
|
6
|
+
# Select modulus (N), generator (g), and one-way hash function (SHA1 or SHA256)
|
7
|
+
#
|
8
|
+
# @param group [Integer] the group size in bits
|
5
9
|
def initialize(group = 2048)
|
6
|
-
|
7
|
-
|
8
|
-
|
10
|
+
raise ArgumentError, 'must be an Integer' unless group.is_a?(Integer)
|
11
|
+
raise ArgumentError, 'must be a known group size' unless [1024, 1536, 2048, 3072, 4096, 6144, 8192].include?(group)
|
12
|
+
|
13
|
+
@N, @g, @hash = Ng(group)
|
14
|
+
@k = calc_k(@N, @g, hash)
|
9
15
|
end
|
10
16
|
|
17
|
+
# Phase 1 : Step 1 : Start the authentication process by generating the
|
18
|
+
# client 'a' and 'A' values. Public 'A' should later be sent along with
|
19
|
+
# the username, to the server verifier to continue the auth process. The
|
20
|
+
# internal secret 'a' value should remain private.
|
21
|
+
#
|
22
|
+
# @return [String] the value of 'A' in hex
|
11
23
|
def start_authentication
|
12
|
-
# Generate a/A private and public components
|
13
24
|
@a ||= SecureRandom.hex(32).hex
|
14
|
-
@A =
|
25
|
+
@A = num_to_hex(calc_A(@a, @N, @g))
|
15
26
|
end
|
16
27
|
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
28
|
+
#
|
29
|
+
# Phase 1 : Step 2 : See Verifier#get_challenge_and_proof(username, xverifier, xsalt, xaa)
|
30
|
+
#
|
31
|
+
|
32
|
+
# Phase 2 : Step 1 : Process the salt and B values provided by the server.
|
33
|
+
#
|
34
|
+
# @param username [String] the client provided authentication username
|
35
|
+
# @param password [String] the client provided authentication password
|
36
|
+
# @param xsalt [String] the server provided salt for the username in hex
|
37
|
+
# @param xbb [String] the server verifier 'B' value in hex
|
38
|
+
# @return [String] the client 'M' value in hex
|
20
39
|
def process_challenge(username, password, xsalt, xbb)
|
40
|
+
raise ArgumentError, 'username must be a string' unless username.is_a?(String) && !username.empty?
|
41
|
+
raise ArgumentError, 'password must be a string' unless password.is_a?(String) && !password.empty?
|
42
|
+
raise ArgumentError, 'xsalt must be a string' unless xsalt.is_a?(String)
|
43
|
+
raise ArgumentError, 'xsalt must be a hex string' unless xsalt =~ /^[a-fA-F0-9]+$/
|
44
|
+
raise ArgumentError, 'xbb must be a string' unless xbb.is_a?(String)
|
45
|
+
raise ArgumentError, 'xbb must be a hex string' unless xbb =~ /^[a-fA-F0-9]+$/
|
46
|
+
|
47
|
+
# Convert the 'B' hex value to an Integer
|
21
48
|
bb = xbb.to_i(16)
|
22
49
|
|
23
50
|
# SRP-6a safety check
|
24
|
-
return false if (bb % @N)
|
51
|
+
return false if (bb % @N).zero?
|
25
52
|
|
26
|
-
x =
|
27
|
-
u =
|
53
|
+
x = calc_x(username, password, xsalt, hash)
|
54
|
+
u = calc_u(@A, xbb, @N, hash)
|
28
55
|
|
29
56
|
# SRP-6a safety check
|
30
|
-
return false if u
|
57
|
+
return false if u.zero?
|
31
58
|
|
32
|
-
#
|
33
|
-
@S =
|
34
|
-
@K =
|
59
|
+
# Calculate session key 'S' and secret key 'K'
|
60
|
+
@S = num_to_hex(calc_client_S(bb, @a, @k, x, u, @N, @g))
|
61
|
+
@K = sha_hex(@S, hash)
|
35
62
|
|
36
|
-
#
|
37
|
-
@M =
|
63
|
+
# Calculate the 'M' matcher
|
64
|
+
@M = calc_M(@A, xbb, @K, hash)
|
38
65
|
|
39
|
-
#
|
40
|
-
@H_AMK =
|
66
|
+
# Calculate the H(A,M,K) verifier
|
67
|
+
@H_AMK = num_to_hex(calc_H_AMK(@A, @M, @K, hash))
|
41
68
|
|
69
|
+
# Return the 'M' matcher to be sent to the server
|
42
70
|
@M
|
43
71
|
end
|
44
72
|
|
73
|
+
#
|
74
|
+
# Phase 2 : Step 2 : See Verifier#verify_session(proof, client_M)
|
75
|
+
#
|
76
|
+
|
77
|
+
# Phase 2 : Step 3 : Verify that the server provided H(A,M,K) value
|
78
|
+
# matches the client generated version. This is the last step of mutual
|
79
|
+
# authentication and confirms that the client and server have
|
80
|
+
# completed the auth process. The comparison of local and server
|
81
|
+
# H_AMK values is done using a secure constant-time comparison
|
82
|
+
# method so as not to leak information.
|
83
|
+
#
|
84
|
+
# @param server_HAMK [String] the server provided H_AMK in hex
|
85
|
+
# @return [true,false] returns true if the server and client agree on the H_AMK value, false if not
|
45
86
|
def verify(server_HAMK)
|
46
|
-
return false unless @H_AMK
|
47
|
-
|
87
|
+
return false unless @H_AMK && server_HAMK
|
88
|
+
return false unless server_HAMK.is_a?(String)
|
89
|
+
return false unless server_HAMK =~ /^[a-fA-F0-9]+$/
|
90
|
+
|
91
|
+
# Hash the comparison params to ensure that both strings
|
92
|
+
# being compared are equal length 32 Byte strings.
|
93
|
+
secure_compare(Digest::SHA256.hexdigest(@H_AMK), Digest::SHA256.hexdigest(server_HAMK))
|
48
94
|
end
|
49
95
|
end
|
50
96
|
end
|