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.
@@ -1,72 +1,123 @@
1
1
  module SIRP
2
2
  class Verifier
3
+ include SIRP
3
4
  attr_reader :N, :g, :k, :A, :B, :b, :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
- # select modulus (N) and generator (g)
7
- @N, @g, @hash = SIRP.Ng(group)
8
- @k = SIRP.calc_k(@N, @g, hash)
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
 
11
- # Initial user creation for the persistance layer.
12
- # Not part of the authentication process.
13
- # Returns { <username>, <password verifier>, <salt> }
17
+ # Phase 0 ; Generate a verifier and salt client-side. This should only be
18
+ # used during the initial user registration process. All three values
19
+ # should be provided as attributes in the user registration process. The
20
+ # verifier and salt should be persisted server-side. The verifier
21
+ # should be protected and never made public or given to any user.
22
+ # The salt should be returned to any user requesting it to start
23
+ # Phase 1 of the authentication process.
24
+ #
25
+ # @param username [String] the authentication username
26
+ # @param password [String] the authentication password
27
+ # @return [Hash] a Hash of the username, verifier, and salt
14
28
  def generate_userauth(username, password)
29
+ raise ArgumentError, 'username must be a string' unless username.is_a?(String) && !username.empty?
30
+ raise ArgumentError, 'password must be a string' unless password.is_a?(String) && !password.empty?
31
+
15
32
  @salt ||= SecureRandom.hex(10)
16
- x = SIRP.calc_x(username, password, @salt, hash)
17
- v = SIRP.calc_v(x, @N, @g)
18
- { username: username, verifier: SIRP.num_to_hex(v), salt: @salt }
33
+ x = calc_x(username, password, @salt, hash)
34
+ v = calc_v(x, @N, @g)
35
+ { username: username, verifier: num_to_hex(v), salt: @salt }
19
36
  end
20
37
 
21
- # Authentication phase 1 - create challenge.
22
- # Returns Hash with challenge for client and proof to be stored on server.
23
- # Parameters should be given in hex.
38
+ # Phase 1 : Step 2 : Create a challenge for the client, and a proof to be stored
39
+ # on the server for later use when verifying the client response.
40
+ #
41
+ # @param username [String] the client provided authentication username
42
+ # @param xverifier [String] the server stored verifier for the username in hex
43
+ # @param xsalt [String] the server stored salt for the username in hex
44
+ # @param xaa [String] the client provided 'A' value in hex
45
+ # @return [Hash] a Hash with the challenge for the client and a proof for the server
24
46
  def get_challenge_and_proof(username, xverifier, xsalt, xaa)
47
+ raise ArgumentError, 'username must be a string' unless username.is_a?(String) && !username.empty?
48
+ raise ArgumentError, 'xverifier must be a string' unless xverifier.is_a?(String)
49
+ raise ArgumentError, 'xverifier must be a hex string' unless xverifier =~ /^[a-fA-F0-9]+$/
50
+ raise ArgumentError, 'xsalt must be a string' unless xsalt.is_a?(String)
51
+ raise ArgumentError, 'xsalt must be a hex string' unless xsalt =~ /^[a-fA-F0-9]+$/
52
+ raise ArgumentError, 'xaa must be a string' unless xaa.is_a?(String)
53
+ raise ArgumentError, 'xaa must be a hex string' unless xaa =~ /^[a-fA-F0-9]+$/
54
+
25
55
  # SRP-6a safety check
26
- return false if (xaa.to_i(16) % @N) == 0
27
- generate_B(xverifier)
56
+ return false if (xaa.to_i(16) % @N).zero?
57
+
58
+ # Generate b and B
59
+ v = xverifier.to_i(16)
60
+ @b ||= SecureRandom.hex(32).hex
61
+ @B = num_to_hex(calc_B(@b, k, v, @N, @g))
28
62
 
29
63
  {
30
64
  challenge: { B: @B, salt: xsalt },
31
- proof: { A: xaa, B: @B, b: SIRP.num_to_hex(@b), I: username, s: xsalt, v: xverifier }
65
+ proof: { A: xaa, B: @B, b: num_to_hex(@b), I: username, s: xsalt, v: xverifier }
32
66
  }
33
67
  end
34
68
 
35
- # returns H_AMK on success, false on failure
36
- # User -> Host: M = H(H(N) xor H(g), H(I), s, A, B, K)
37
- # Host -> User: H(A, M, K)
69
+ #
70
+ # Phase 2 : Step 1 : See Client#start_authentication
71
+ #
72
+
73
+ # Phase 2 : Step 2 : Use the server stored proof and the client provided 'M' value.
74
+ # Calculates a server 'M' value and compares it to the client provided one,
75
+ # and if they match the client and server have negotiated equal secrets.
76
+ # Returns a H(A, M, K) value on success and false on failure.
77
+ #
78
+ # Sets the @K value, which is the client and server negotiated secret key
79
+ # if verification succeeds. This can be used to derive strong encryption keys
80
+ # for later use. The client independently calculates the same @K value as well.
81
+ #
82
+ # If authentication fails the H_AMK value must not be provided to the client.
83
+ #
84
+ # @param proof [Hash] the server stored proof Hash with keys A, B, b, I, s, v
85
+ # @param client_M [String] the client provided 'M' value in hex
86
+ # @return [String, false] the H_AMK value in hex for the client, or false if verification failed
38
87
  def verify_session(proof, client_M)
88
+ raise ArgumentError, 'proof must be a hash' unless proof.is_a?(Hash)
89
+ # gracefully handle string or symbol keys
90
+ Hashie.symbolize_keys!(proof)
91
+ raise ArgumentError, 'proof must have required hash keys' unless proof.keys == [:A, :B, :b, :I, :s, :v]
92
+ raise ArgumentError, 'client_M must be a string' unless client_M.is_a?(String)
93
+ raise ArgumentError, 'client_M must be a hex string' unless client_M =~ /^[a-fA-F0-9]+$/
94
+
39
95
  @A = proof[:A]
40
96
  @B = proof[:B]
41
97
  @b = proof[:b].to_i(16)
42
98
  v = proof[:v].to_i(16)
43
99
 
44
- u = SIRP.calc_u(@A, @B, @N, hash)
100
+ u = calc_u(@A, @B, @N, hash)
45
101
 
46
102
  # SRP-6a safety check
47
- return false if u == 0
103
+ return false if u.zero?
48
104
 
49
- # calculate session key
50
- @S = SIRP.num_to_hex(SIRP.calc_server_S(@A.to_i(16), @b, v, u, @N))
51
- @K = SIRP.sha_hex(@S, hash)
105
+ # Calculate session key 'S' and secret key 'K'
106
+ @S = num_to_hex(calc_server_S(@A.to_i(16), @b, v, u, @N))
107
+ @K = sha_hex(@S, hash)
52
108
 
53
- # calculate match
54
- @M = SIRP.calc_M(@A, @B, @K, hash)
109
+ # Calculate the 'M' matcher
110
+ @M = calc_M(@A, @B, @K, hash)
55
111
 
56
- if @M == client_M
57
- # authentication succeeded
58
- @H_AMK = SIRP.num_to_hex(SIRP.calc_H_AMK(@A, @M, @K, hash))
112
+ # Secure constant time comparison, hash the params to ensure
113
+ # that both strings being compared are equal length 32 Byte strings.
114
+ if secure_compare(Digest::SHA256.hexdigest(@M), Digest::SHA256.hexdigest(client_M))
115
+ # Authentication succeeded, Calculate the H(A,M,K) verifier
116
+ @H_AMK = num_to_hex(calc_H_AMK(@A, @M, @K, hash))
59
117
  else
118
+ # Authentication failed
60
119
  false
61
120
  end
62
121
  end
63
-
64
- # generates challenge
65
- # input verifier in hex
66
- def generate_B(xverifier)
67
- v = xverifier.to_i(16)
68
- @b ||= SecureRandom.hex(32).hex
69
- @B = SIRP.num_to_hex(SIRP.calc_B(@b, k, v, @N, @g))
70
- end
71
122
  end
72
123
  end
@@ -1,3 +1,3 @@
1
1
  module SIRP
2
- VERSION = '2.0.0.pre'.freeze
2
+ VERSION = '2.0.0'.freeze
3
3
  end
@@ -36,10 +36,11 @@ Gem::Specification.new do |spec|
36
36
 
37
37
  # See : https://bugs.ruby-lang.org/issues/9569
38
38
  spec.add_runtime_dependency 'rbnacl-libsodium', '~> 1.0'
39
- spec.add_runtime_dependency 'securer_randomer', '~> 0.1.0'
39
+ spec.add_runtime_dependency 'sysrandom', '~> 1.0'
40
+ spec.add_runtime_dependency 'hashie', '~> 3.4'
40
41
 
41
- spec.add_development_dependency 'bundler', '~> 1.12'
42
- spec.add_development_dependency 'rake', '~> 11.0'
42
+ spec.add_development_dependency 'bundler'
43
+ spec.add_development_dependency 'rake'
43
44
  spec.add_development_dependency 'rspec', '~> 3.4'
44
45
  spec.add_development_dependency 'pry', '~> 0.10'
45
46
  spec.add_development_dependency 'coveralls', '~> 0.8'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sirp
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Glenn Rempe
@@ -31,7 +31,7 @@ cert_chain:
31
31
  zieXiXZSAojfFx9g91fKdIrlPbInHU/BaCxXSLBwvOM0drE+c2ue9X8gB55XAhzX
32
32
  37oBiw==
33
33
  -----END CERTIFICATE-----
34
- date: 2016-05-13 00:00:00.000000000 Z
34
+ date: 2016-09-21 00:00:00.000000000 Z
35
35
  dependencies:
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rbnacl-libsodium
@@ -48,47 +48,61 @@ dependencies:
48
48
  - !ruby/object:Gem::Version
49
49
  version: '1.0'
50
50
  - !ruby/object:Gem::Dependency
51
- name: securer_randomer
51
+ name: sysrandom
52
52
  requirement: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
- version: 0.1.0
56
+ version: '1.0'
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: 0.1.0
63
+ version: '1.0'
64
64
  - !ruby/object:Gem::Dependency
65
- name: bundler
65
+ name: hashie
66
66
  requirement: !ruby/object:Gem::Requirement
67
67
  requirements:
68
68
  - - "~>"
69
69
  - !ruby/object:Gem::Version
70
- version: '1.12'
71
- type: :development
70
+ version: '3.4'
71
+ type: :runtime
72
72
  prerelease: false
73
73
  version_requirements: !ruby/object:Gem::Requirement
74
74
  requirements:
75
75
  - - "~>"
76
76
  - !ruby/object:Gem::Version
77
- version: '1.12'
77
+ version: '3.4'
78
+ - !ruby/object:Gem::Dependency
79
+ name: bundler
80
+ requirement: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ type: :development
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
78
92
  - !ruby/object:Gem::Dependency
79
93
  name: rake
80
94
  requirement: !ruby/object:Gem::Requirement
81
95
  requirements:
82
- - - "~>"
96
+ - - ">="
83
97
  - !ruby/object:Gem::Version
84
- version: '11.0'
98
+ version: '0'
85
99
  type: :development
86
100
  prerelease: false
87
101
  version_requirements: !ruby/object:Gem::Requirement
88
102
  requirements:
89
- - - "~>"
103
+ - - ">="
90
104
  - !ruby/object:Gem::Version
91
- version: '11.0'
105
+ version: '0'
92
106
  - !ruby/object:Gem::Dependency
93
107
  name: rspec
94
108
  requirement: !ruby/object:Gem::Requirement
@@ -173,7 +187,9 @@ files:
173
187
  - ".gitignore"
174
188
  - ".rubocop.yml"
175
189
  - ".travis.yml"
190
+ - ".yardopts"
176
191
  - CHANGELOG.md
192
+ - CODE_OF_CONDUCT.md
177
193
  - Gemfile
178
194
  - LICENSE.txt
179
195
  - README.md
@@ -184,16 +200,9 @@ files:
184
200
  - certs/gem-public_cert_grempe.pem
185
201
  - docs/rfc2945.txt
186
202
  - docs/rfc5054.txt
187
- - examples/Gemfile
188
- - examples/README.md
189
- - examples/clients/javascript/.gitignore
190
- - examples/clients/javascript/app.js
191
- - examples/clients/javascript/index.html
192
- - examples/clients/javascript/package.json
193
- - examples/clients/ruby/client.rb
194
- - examples/server.rb
195
203
  - lib/sirp.rb
196
204
  - lib/sirp/client.rb
205
+ - lib/sirp/parameters.rb
197
206
  - lib/sirp/sirp.rb
198
207
  - lib/sirp/verifier.rb
199
208
  - lib/sirp/version.rb
@@ -213,9 +222,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
213
222
  version: 2.1.0
214
223
  required_rubygems_version: !ruby/object:Gem::Requirement
215
224
  requirements:
216
- - - ">"
225
+ - - ">="
217
226
  - !ruby/object:Gem::Version
218
- version: 1.3.1
227
+ version: '0'
219
228
  requirements: []
220
229
  rubyforge_project:
221
230
  rubygems_version: 2.5.1
@@ -223,4 +232,3 @@ signing_key:
223
232
  specification_version: 4
224
233
  summary: Secure (interoperable) Remote Password Auth (SRP-6a)
225
234
  test_files: []
226
- has_rdoc:
metadata.gz.sig CHANGED
Binary file
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'puma'
4
- gem 'sinatra'
5
- gem 'http'
6
- gem 'sirp', path: '../'
@@ -1,34 +0,0 @@
1
- # SiRP : Secure (interoperable) Remote Password : Example
2
-
3
- ## Install Ruby Dependencies
4
-
5
- ```
6
- $ cd examples
7
- $ bundle install
8
- ```
9
-
10
- ## Run Server
11
-
12
- ```sh
13
- # run this in the first terminal window
14
- $ ./server.rb
15
- ```
16
-
17
- ## Authenticate with Ruby Client
18
-
19
- ```sh
20
- # run this in the second terminal window
21
- $ cd clients/ruby/
22
- $ ./client.rb
23
- ```
24
-
25
- ## Authenticate with JavaScript Client
26
-
27
- ```sh
28
- # see the output of the JS client in the browser's JS console
29
- $ cd clients/javascript/
30
- $ npm install
31
- $ open index.html
32
- ```
33
-
34
- You can find other username : password combinations in `server.rb`
@@ -1 +0,0 @@
1
- /node_modules/
@@ -1,59 +0,0 @@
1
- var client = new jsrp.client()
2
- var serverAddr = 'http://localhost:4567/authenticate'
3
- var username = 'leonardo'
4
- var password = 'capricciosa'
5
-
6
- $(document).ready(function () {
7
- 'use strict'
8
-
9
- console.log('Starting Authentication')
10
-
11
- $(document).ajaxError(function (event, request, settings) {
12
- console.log(event)
13
- console.log(request)
14
- console.log(settings)
15
- })
16
-
17
- client.init({ username: username, password: password, length: 4096 }, function () {
18
- client.createVerifier(function (err, result) {
19
- // result will contain the necessary values the server needs to
20
- // authenticate this user in the future.
21
- // sendSaltToServer(result.salt)
22
- // sendVerifierToServer(result.verifier)
23
-
24
- if (err) {
25
- console.log(err.stack);
26
- }
27
-
28
- var A = client.getPublicKey()
29
-
30
- // Auth Phase 1
31
- console.log('P1 : Sending username and A to server')
32
- $.post(serverAddr, { username: username, A: A }, function (data) {
33
- console.log('P1 : Received salt : ', data.salt)
34
- console.log('P1 : Received B : ', data.B)
35
- client.setSalt(data.salt)
36
- client.setServerPublicKey(data.B)
37
-
38
- var clientM = client.getProof()
39
- console.log('P1 : calculated client M : ', clientM)
40
-
41
- // Auth Phase 2
42
- console.log('P2 : Sending username and M to server')
43
- $.post(serverAddr, { username: username, M: clientM }, function (data) {
44
- console.log('P2 : Received server H_AMK : ', data.H_AMK)
45
- if (client.checkServerProof(data.H_AMK)) {
46
- console.log('P2 : Client and server H_AMK match. Authenticated!')
47
- console.log('P2 : Client and server negotiated shared key K : ', client.getSharedKey())
48
- $('#status').html('Authenticated!')
49
- $('#username').html(username)
50
- $('#K').html(client.getSharedKey())
51
- } else {
52
- $('#status').html('Authenticated FAILED')
53
- console.log('P2 : Client and server H_AMK DO NOT match. Authentication failed!')
54
- }
55
- }, 'json')
56
- }, 'json')
57
- })
58
- })
59
- })
@@ -1,23 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <title>Secure Remote Password JS Client Example</title>
6
- </head>
7
- <body>
8
- <h1>Secure Remote Password JS Client Example</h1>
9
-
10
- <p>
11
- Open the JavaScript console to see more output. Any shared secret
12
- negotiated on successful authentication will be displayed below.
13
- </p>
14
-
15
- <p id='status'></p>
16
- <p id='username'></p>
17
- <p id='K'></p>
18
-
19
- <script type="text/javascript" src="node_modules/jquery/dist/jquery.min.js"></script>
20
- <script type="text/javascript" src="node_modules/jsrp/jsrp-browser.js"></script>
21
- <script type="text/javascript" src="app.js"></script>
22
- </body>
23
- </html>
@@ -1,15 +0,0 @@
1
- {
2
- "name": "javascript",
3
- "version": "1.0.0",
4
- "description": "",
5
- "main": "app.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "author": "",
10
- "license": "New BSD",
11
- "dependencies": {
12
- "jquery": "^2.2.3",
13
- "jsrp": "^0.2.0"
14
- }
15
- }