1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
module SRP
class Session
include SRP::Util
attr_accessor :user
def initialize(user, aa=nil)
@user = user
aa ? initialize_server(aa) : initialize_client
end
# client -> server: I, A = g^a
def handshake(server)
@bb = server.handshake(user.username, aa)
end
# client -> server: M = H(H(N) xor H(g), H(I), s, A, B, K)
def validate(server)
server.validate(m)
end
def authenticate!(client_auth)
authenticate(client_auth) || raise(SRP::WrongPassword)
end
def authenticate(client_auth)
if(client_auth == m)
@authenticated = true
return @user
end
end
def to_hash
if @authenticated
{ :M2 => m2.to_s(16) }
else
{ :B => bb.to_s(16),
# :b => @b.to_s(16), # only use for debugging
:salt => @user.salt.to_s(16)
}
end
end
def to_json(options={})
to_hash.to_json(options)
end
# for debugging use:
def internal_state
{
username: @user.username,
salt: @user.salt.to_s(16),
verifier: @user.verifier.to_s(16),
aa: aa.to_s(16),
bb: bb.to_s(16),
s: secret.to_s(16),
k: k.to_s(16),
m: m.to_s(16),
m2: m2.to_s(16)
}
end
def aa
@aa ||= modpow(GENERATOR, @a) # A = g^a (mod N)
end
# B = g^b + k v (mod N)
def bb
@bb ||= (modpow(GENERATOR, @b) + multiplier * @user.verifier) % BIG_PRIME_N
end
protected
# only seed b for testing purposes.
def initialize_server(aa, ephemeral = nil)
@aa = aa
@b = ephemeral || bigrand(32).hex
end
def initialize_client
@a = bigrand(32).hex
# bb will be set during handshake.
end
def secret
return client_secret if @a
return server_secret if @b
end
# client: K = H( (B - kg^x) ^ (a + ux) )
def client_secret
base = bb
# base += BIG_PRIME_N * @multiplier
base -= modpow(GENERATOR, @user.private_key) * multiplier
base = base % BIG_PRIME_N
modpow(base, @user.private_key * u + @a)
end
# server: K = H( (Av^u) ^ b )
# do not cache this - it's secret and someone might store the
# session in a CookieStore
def server_secret
base = (modpow(@user.verifier, u) * aa) % BIG_PRIME_N
modpow(base, @b)
end
# SRP 6a uses
# M = H(H(N) xor H(g), H(I), s, A, B, K)
def m
@m ||= sha256_int(n_xor_g_long, login_hash, @user.salt, aa, bb, k).hex
end
def m2
@m2 ||= sha256_int(aa, m, k).hex
end
def k
@k ||= sha256_int(secret).hex
end
def n_xor_g_long
@n_xor_g_long ||= hn_xor_hg.bytes.map{|b| "%02x" % b.ord}.join.hex
end
def login_hash
@login_hash ||= sha256_str(@user.username).hex
end
def u
@u ||= sha256_int(aa, bb).hex
end
end
end
|