sirp 2.0.0.pre → 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
- 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
|