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.
@@ -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
- }