ruby_home-srp 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/ruby_home-srp.rb +180 -0
- data/lib/ruby_home-srp/version.rb +5 -0
- data/spec/spec_helper.rb +13 -0
- metadata +91 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 01aa88f20f9d30e522a70b7b9b5fb46c0a88c72e17ed8497508673a49e3f4c81
|
4
|
+
data.tar.gz: be1162da69b86dbc69ac9a54f878ff10ff825691549058028e110c8f174f9e8d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: be13ad8ed43883b4c1bda4362481edcfe4ea1873afba34942157bf2a44b114672db9f783d7aa96dd6b5a0a640590b3b89b055381b2cb2e2bdcc63a53111fd4ce
|
7
|
+
data.tar.gz: ffffd98cda85809b6da6b921d39093acd974f379d9c6b877a5ab3a97b3fffebc3ade15179309ec837f82ec0203950c5793fbcc1759cea491972f1532109e4be6
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'srp'
|
3
|
+
|
4
|
+
module RubyHome
|
5
|
+
module SRP
|
6
|
+
class << self
|
7
|
+
::SRP.singleton_methods.each do |m|
|
8
|
+
define_method m, ::SRP.method(m).to_proc
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def sha512_hex(h)
|
14
|
+
OpenSSL::Digest::SHA512.hexdigest([h].pack('H*'))
|
15
|
+
end
|
16
|
+
|
17
|
+
def sha512_str(s)
|
18
|
+
OpenSSL::Digest::SHA512.hexdigest(s)
|
19
|
+
end
|
20
|
+
|
21
|
+
# hashing function with padding.
|
22
|
+
# Input is prefixed with 0 to meet N hex width.
|
23
|
+
def H(n, *a)
|
24
|
+
nlen = 2 * ((('%x' % [n]).length * 4 + 7) >> 3)
|
25
|
+
hashin = a.map {|s|
|
26
|
+
next unless s
|
27
|
+
shex = s.class == String ? s : '%x' % s
|
28
|
+
if shex.length > nlen
|
29
|
+
raise 'Bit width does not match - client uses different prime'
|
30
|
+
end
|
31
|
+
'0' * (nlen - shex.length) + shex
|
32
|
+
}.join('')
|
33
|
+
sha512_hex(hashin).hex % n
|
34
|
+
end
|
35
|
+
|
36
|
+
# Multiplier parameter
|
37
|
+
# k = H(N, g) (in SRP-6a)
|
38
|
+
def calc_k(n, g)
|
39
|
+
H(n, n, g)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Private key (derived from username, raw password and salt)
|
43
|
+
# x = H(salt || H(username || ':' || password))
|
44
|
+
def calc_x(username, password, salt)
|
45
|
+
sha512_hex(salt + sha512_str([username, password].join(':'))).hex
|
46
|
+
end
|
47
|
+
|
48
|
+
# Random scrambling parameter
|
49
|
+
# u = H(A, B)
|
50
|
+
def calc_u(xaa, xbb, n)
|
51
|
+
H(n, xaa, xbb)
|
52
|
+
end
|
53
|
+
|
54
|
+
# M = H(H(N) xor H(g), H(I), s, A, B, K)
|
55
|
+
def calc_M(username, xsalt, xaa, xbb, xkk, n, g)
|
56
|
+
hn = sha512_hex('%x' % n).hex
|
57
|
+
hg = sha512_hex(g).hex
|
58
|
+
hxor = '%x' % (hn ^ hg)
|
59
|
+
hi = sha512_str(username)
|
60
|
+
|
61
|
+
hashin = [hxor, hi, xsalt, xaa, xbb, xkk].join
|
62
|
+
sha512_hex(hashin).hex % n
|
63
|
+
end
|
64
|
+
|
65
|
+
# H(A, M, K)
|
66
|
+
def calc_H_AMK(xaa, xmm, xkk, n, g)
|
67
|
+
hashin = [xaa, xmm, xkk].join()
|
68
|
+
sha512_hex(hashin).hex % n
|
69
|
+
end
|
70
|
+
|
71
|
+
def Ng(group)
|
72
|
+
case group
|
73
|
+
when 3072
|
74
|
+
@N = %w{
|
75
|
+
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
|
76
|
+
8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
|
77
|
+
302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
|
78
|
+
A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
|
79
|
+
49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
|
80
|
+
FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
|
81
|
+
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
|
82
|
+
180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
|
83
|
+
3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
|
84
|
+
04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
|
85
|
+
B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
|
86
|
+
1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
|
87
|
+
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
|
88
|
+
E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF
|
89
|
+
}.join.hex
|
90
|
+
@g = '05'
|
91
|
+
else
|
92
|
+
raise NotImplementedError
|
93
|
+
end
|
94
|
+
return [@N, @g]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Verifier < ::SRP::Verifier
|
99
|
+
attr_reader :u
|
100
|
+
attr_writer :salt
|
101
|
+
|
102
|
+
def initialize group=3072
|
103
|
+
# select modulus (N) and generator (g)
|
104
|
+
@N, @g = SRP.Ng group
|
105
|
+
@k = SRP.calc_k(@N, @g)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Initial user creation for the persistance layer.
|
109
|
+
# Not part of the authentication process.
|
110
|
+
# Returns { <username>, <password verifier>, <salt> }
|
111
|
+
def generate_userauth username, password
|
112
|
+
@salt ||= random_salt
|
113
|
+
x = SRP.calc_x(username, password, @salt)
|
114
|
+
v = SRP.calc_v(x, @N, @g.hex)
|
115
|
+
return {:username => username, :verifier => '%x' % v, :salt => @salt}
|
116
|
+
end
|
117
|
+
|
118
|
+
# Authentication phase 1 - create challenge.
|
119
|
+
# Returns Hash with challenge for client and proof to be stored on server.
|
120
|
+
# Parameters should be given in hex.
|
121
|
+
def get_challenge_and_proof username, xverifier, xsalt
|
122
|
+
generate_B(xverifier)
|
123
|
+
return {
|
124
|
+
:challenge => {:B => @B, :salt => xsalt},
|
125
|
+
:proof => {:B => @B, :b => '%x' % @b, :I => username, :s => xsalt, :v => xverifier}
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
# returns H_AMK on success, None on failure
|
130
|
+
# User -> Host: M = H(H(N) xor H(g), H(I), s, A, B, K)
|
131
|
+
# Host -> User: H(A, M, K)
|
132
|
+
def verify_session proof, client_M
|
133
|
+
@A = proof[:A]
|
134
|
+
@B = proof[:B]
|
135
|
+
@b = proof[:b].to_i(16)
|
136
|
+
username = proof[:I]
|
137
|
+
xsalt = proof[:s]
|
138
|
+
v = proof[:v].to_i(16)
|
139
|
+
|
140
|
+
@u = SRP.calc_u(@A, @B, @N)
|
141
|
+
# SRP-6a safety check
|
142
|
+
return false if @u == 0
|
143
|
+
|
144
|
+
# calculate session key
|
145
|
+
@S = '%x' % SRP.calc_server_S(@A.to_i(16), @b, v, @u, @N)
|
146
|
+
@K = SRP.sha512_hex(@S)
|
147
|
+
|
148
|
+
# calculate match
|
149
|
+
@M = '%x' % SRP.calc_M(username, xsalt, @A, @B, @K, @N, @g)
|
150
|
+
|
151
|
+
if @M == client_M
|
152
|
+
# authentication succeeded
|
153
|
+
@H_AMK = '%x' % SRP.calc_H_AMK(@A, @M, @K, @N, @g)
|
154
|
+
return @H_AMK
|
155
|
+
end
|
156
|
+
return false
|
157
|
+
end
|
158
|
+
|
159
|
+
def random_salt
|
160
|
+
SecureRandom.hex(16)
|
161
|
+
end
|
162
|
+
|
163
|
+
def random_bignum
|
164
|
+
SecureRandom.hex(32).hex
|
165
|
+
end
|
166
|
+
|
167
|
+
def u
|
168
|
+
'%x' % @u
|
169
|
+
end
|
170
|
+
|
171
|
+
# generates challenge
|
172
|
+
# input verifier in hex
|
173
|
+
def generate_B xverifier
|
174
|
+
v = xverifier.to_i(16)
|
175
|
+
@b ||= random_bignum
|
176
|
+
@B = '%x' % SRP.calc_B(@b, k, v, @N, @g.hex)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
# Disable RSpec exposing methods globally on `Module` and `main`
|
6
|
+
config.disable_monkey_patching!
|
7
|
+
|
8
|
+
config.expect_with :rspec do |c|
|
9
|
+
c.syntax = :expect
|
10
|
+
end
|
11
|
+
|
12
|
+
config.order = :random
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby_home-srp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Karl Entwistle
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-03-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: srp-rb
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '12.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '12.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description: 'Secure Remote Password protocol (SRP-6a) with HAP modifications
|
56
|
+
|
57
|
+
'
|
58
|
+
email:
|
59
|
+
- karl@entwistle.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- lib/ruby_home-srp.rb
|
65
|
+
- lib/ruby_home-srp/version.rb
|
66
|
+
- spec/spec_helper.rb
|
67
|
+
homepage: https://github.com/karlentwistle/ruby_home-srp
|
68
|
+
licenses: []
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 2.7.5
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: Secure Remote Password protocol SRP-6a-HAP
|
90
|
+
test_files:
|
91
|
+
- spec/spec_helper.rb
|