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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
#
# Here are some very stripped down helper methods for SRP, useful only for
# testing the client side.
#
require 'digest'
require 'openssl'
require 'securerandom'
require 'base64'
module SRP
##
## UTIL
##
module Util
PRIME_N = <<-EOS.split.join.hex
115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3
EOS
BIG_PRIME_N = <<-EOS.split.join.hex # 1024 bits modulus (N)
eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c25657
6d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089da
d15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e5
7ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb
06e3
EOS
GENERATOR = 2 # g
def hn_xor_hg
byte_xor_hex(sha256_int(BIG_PRIME_N), sha256_int(GENERATOR))
end
# a^n (mod m)
def modpow(a, n, m = BIG_PRIME_N)
r = 1
while true
r = r * a % m if n[0] == 1
n >>= 1
return r if n == 0
a = a * a % m
end
end
# Hashes the (long) int args
def sha256_int(*args)
sha256_hex(*args.map{|a| "%02x" % a})
end
# Hashes the hex args
def sha256_hex(*args)
h = args.map{|a| a.length.odd? ? "0#{a}" : a }.join('')
sha256_str([h].pack('H*'))
end
def sha256_str(s)
Digest::SHA2.hexdigest(s)
end
def bigrand(bytes)
OpenSSL::Random.random_bytes(bytes).unpack("H*")[0]
end
def multiplier
@muliplier ||= calculate_multiplier
end
protected
def calculate_multiplier
sha256_int(BIG_PRIME_N, GENERATOR).hex
end
def byte_xor_hex(a, b)
a = [a].pack('H*')
b = [b].pack('H*')
a.bytes.each_with_index.map do |a_byte, i|
(a_byte ^ (b[i].ord || 0)).chr
end.join
end
end
##
## SESSION
##
class Session
include SRP::Util
attr_accessor :user
attr_accessor :bb
def initialize(user, aa=nil)
@user = user
@a = bigrand(32).hex
end
def m
@m ||= sha256_hex(n_xor_g_long, login_hash, @user.salt.to_s(16), aa, bb, k)
end
def aa
@aa ||= modpow(GENERATOR, @a).to_s(16) # A = g^a (mod N)
end
protected
# client: K = H( (B - kg^x) ^ (a + ux) )
def client_secret
base = bb.hex
base -= modpow(GENERATOR, @user.private_key) * multiplier
base = base % BIG_PRIME_N
modpow(base, @user.private_key * u.hex + @a)
end
def k
@k ||= sha256_int(client_secret)
end
def n_xor_g_long
@n_xor_g_long ||= hn_xor_hg.bytes.map{|b| "%02x" % b.ord}.join
end
def login_hash
@login_hash ||= sha256_str(@user.username)
end
def u
@u ||= sha256_hex(aa, bb)
end
end
##
## Dummy USER
##
class User
include SRP::Util
attr_accessor :username, :password, :salt, :verifier, :id, :session_token, :ok, :deleted
def initialize(username=nil)
@username = username || "tmp_user_" + SecureRandom.urlsafe_base64(10).downcase.gsub(/[_-]/, '')
@password = "password_" + SecureRandom.urlsafe_base64(10)
@salt = bigrand(4).hex
@verifier = modpow(GENERATOR, private_key)
@ok = false
@deleted = false
end
def private_key
@private_key ||= calculate_private_key
end
def to_params
{
'user[login]' => @username,
'user[password_verifier]' => @verifier.to_s(16),
'user[password_salt]' => @salt.to_s(16)
}
end
private
def calculate_private_key
shex = '%x' % [@salt]
inner = sha256_str([@username, @password].join(':'))
sha256_hex(shex, inner).hex
end
end
end
|