srp-rb 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/srp-rb.rb +446 -0
- data/lib/srp-rb/version.rb +5 -0
- data/lib/srp.rb +1 -0
- data/spec/srp_spec.rb +474 -0
- metadata +68 -0
data/lib/srp-rb.rb
ADDED
@@ -0,0 +1,446 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
=begin
|
3
|
+
This is a pure Ruby implementation of the Secure Remote
|
4
|
+
Password protocol (SRP-6a). SRP is a cryptographically
|
5
|
+
strong authentication protocol for password-based, mutual
|
6
|
+
authentication over an insecure network connection.
|
7
|
+
|
8
|
+
References
|
9
|
+
* http://srp.stanford.edu/
|
10
|
+
* http://srp.stanford.edu/demo/demo.html
|
11
|
+
|
12
|
+
Copyright (c) 2012, Mikael Lammentausta
|
13
|
+
All rights reserved.
|
14
|
+
|
15
|
+
Following is the New BSD license:
|
16
|
+
|
17
|
+
Redistribution and use in source and binary forms, with or without
|
18
|
+
modification, are permitted provided that the following conditions are met:
|
19
|
+
* Redistributions of source code must retain the above copyright
|
20
|
+
notice, this list of conditions and the following disclaimer.
|
21
|
+
* Redistributions in binary form must reproduce the above copyright
|
22
|
+
notice, this list of conditions and the following disclaimer in the
|
23
|
+
documentation and/or other materials provided with the distribution.
|
24
|
+
* Neither the name of the Python Software Foundation nor the
|
25
|
+
names of its contributors may be used to endorse or promote products
|
26
|
+
derived from this software without specific prior written permission.
|
27
|
+
|
28
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
29
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
30
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
31
|
+
DISCLAIMED. IN NO EVENT SHALL TOM COCAGNE BE LIABLE FOR ANY
|
32
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
33
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
34
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
35
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
36
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
37
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
38
|
+
|
39
|
+
=end
|
40
|
+
require "digest"
|
41
|
+
require "openssl"
|
42
|
+
require "srp-rb/version"
|
43
|
+
|
44
|
+
module SRP
|
45
|
+
class << self
|
46
|
+
|
47
|
+
def sha1_hex(h)
|
48
|
+
Digest::SHA1.hexdigest([h].pack('H*'))
|
49
|
+
end
|
50
|
+
|
51
|
+
def sha1_str(s)
|
52
|
+
Digest::SHA1.hexdigest(s)
|
53
|
+
end
|
54
|
+
|
55
|
+
def bigrand(bytes)
|
56
|
+
OpenSSL::Random.random_bytes(bytes).unpack("H*")[0]
|
57
|
+
end
|
58
|
+
|
59
|
+
# a^n (mod m)
|
60
|
+
def modpow(a, n, m)
|
61
|
+
r = 1
|
62
|
+
while true
|
63
|
+
r = r * a % m if n[0] == 1
|
64
|
+
n >>= 1
|
65
|
+
return r if n == 0
|
66
|
+
a = a * a % m
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# SHA1 hashing function with padding.
|
71
|
+
# Input is prefixed with 0 to meet N hex width.
|
72
|
+
def H(n, *a)
|
73
|
+
nlen = 2 * ((('%x' % [n]).length * 4 + 7) >> 3)
|
74
|
+
hashin = a.map {|s|
|
75
|
+
next unless s
|
76
|
+
shex = s.class == String ? s : "%x" % s
|
77
|
+
if shex.length > nlen
|
78
|
+
raise "Bit width does not match - client uses different prime"
|
79
|
+
end
|
80
|
+
"0" * (nlen - shex.length) + shex
|
81
|
+
}.join('')
|
82
|
+
sha1_hex(hashin).hex % n
|
83
|
+
end
|
84
|
+
|
85
|
+
# Multiplier parameter
|
86
|
+
# k = H(N, g) (in SRP-6a)
|
87
|
+
def calc_k(n, g)
|
88
|
+
H(n, n, g)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Private key (derived from username, raw password and salt)
|
92
|
+
# x = H(salt || H(username || ':' || password))
|
93
|
+
def calc_x(username, password, salt)
|
94
|
+
spad = if salt.length.odd? then '0' else '' end
|
95
|
+
sha1_hex(spad + salt + sha1_str([username, password].join(':'))).hex
|
96
|
+
end
|
97
|
+
|
98
|
+
# Random scrambling parameter
|
99
|
+
# u = H(A, B)
|
100
|
+
def calc_u(xaa, xbb, n)
|
101
|
+
H(n, xaa, xbb)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Password verifier
|
105
|
+
# v = g^x (mod N)
|
106
|
+
def calc_v(x, n, g)
|
107
|
+
modpow(g, x, n)
|
108
|
+
end
|
109
|
+
|
110
|
+
# A = g^a (mod N)
|
111
|
+
def calc_A(a, n, g)
|
112
|
+
modpow(g, a, n)
|
113
|
+
end
|
114
|
+
|
115
|
+
# B = g^b + k v (mod N)
|
116
|
+
def calc_B(b, k, v, n, g)
|
117
|
+
(modpow(g, b, n) + k * v) % n
|
118
|
+
end
|
119
|
+
|
120
|
+
# Client secret
|
121
|
+
# S = (B - (k * g^x)) ^ (a + (u * x)) % N
|
122
|
+
def calc_client_S(bb, a, k, x, u, n, g)
|
123
|
+
modpow((bb - k * modpow(g, x, n)) % n, (a+ x * u), n)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Server secret
|
127
|
+
# S = (A * v^u) ^ b % N
|
128
|
+
def calc_server_S(aa, b, v, u, n)
|
129
|
+
modpow((modpow(v, u, n) * aa), b, n)
|
130
|
+
end
|
131
|
+
|
132
|
+
# M = H(H(N) xor H(g), H(I), s, A, B, K)
|
133
|
+
def calc_M(username, xsalt, xaa, xbb, xkk, n, g)
|
134
|
+
hn = sha1_hex("%x" % n).hex
|
135
|
+
hg = sha1_hex("%x" % g).hex
|
136
|
+
hxor = "%x" % (hn ^ hg)
|
137
|
+
hi = sha1_str(username)
|
138
|
+
return H(n, hxor, hi, xsalt, xaa, xbb, xkk)
|
139
|
+
end
|
140
|
+
|
141
|
+
# H(A, M, K)
|
142
|
+
def calc_H_AMK(xaa, xmm, xkk, n, g)
|
143
|
+
H(n, xaa, xmm, xkk)
|
144
|
+
end
|
145
|
+
|
146
|
+
def Ng(group)
|
147
|
+
case group
|
148
|
+
when 1024
|
149
|
+
@N = %w{
|
150
|
+
EEAF0AB9 ADB38DD6 9C33F80A FA8FC5E8 60726187 75FF3C0B 9EA2314C
|
151
|
+
9C256576 D674DF74 96EA81D3 383B4813 D692C6E0 E0D5D8E2 50B98BE4
|
152
|
+
8E495C1D 6089DAD1 5DC7D7B4 6154D6B6 CE8EF4AD 69B15D49 82559B29
|
153
|
+
7BCF1885 C529F566 660E57EC 68EDBC3C 05726CC0 2FD4CBF4 976EAA9A
|
154
|
+
FD5138FE 8376435B 9FC61D2F C0EB06E3
|
155
|
+
}.join.hex
|
156
|
+
@g = 2
|
157
|
+
|
158
|
+
when 1536
|
159
|
+
@N = %w{
|
160
|
+
9DEF3CAF B939277A B1F12A86 17A47BBB DBA51DF4 99AC4C80 BEEEA961
|
161
|
+
4B19CC4D 5F4F5F55 6E27CBDE 51C6A94B E4607A29 1558903B A0D0F843
|
162
|
+
80B655BB 9A22E8DC DF028A7C EC67F0D0 8134B1C8 B9798914 9B609E0B
|
163
|
+
E3BAB63D 47548381 DBC5B1FC 764E3F4B 53DD9DA1 158BFD3E 2B9C8CF5
|
164
|
+
6EDF0195 39349627 DB2FD53D 24B7C486 65772E43 7D6C7F8C E442734A
|
165
|
+
F7CCB7AE 837C264A E3A9BEB8 7F8A2FE9 B8B5292E 5A021FFF 5E91479E
|
166
|
+
8CE7A28C 2442C6F3 15180F93 499A234D CF76E3FE D135F9BB
|
167
|
+
}.join.hex
|
168
|
+
@g = 2
|
169
|
+
|
170
|
+
when 2048
|
171
|
+
@N = %w{
|
172
|
+
AC6BDB41 324A9A9B F166DE5E 1389582F AF72B665 1987EE07 FC319294
|
173
|
+
3DB56050 A37329CB B4A099ED 8193E075 7767A13D D52312AB 4B03310D
|
174
|
+
CD7F48A9 DA04FD50 E8083969 EDB767B0 CF609517 9A163AB3 661A05FB
|
175
|
+
D5FAAAE8 2918A996 2F0B93B8 55F97993 EC975EEA A80D740A DBF4FF74
|
176
|
+
7359D041 D5C33EA7 1D281E44 6B14773B CA97B43A 23FB8016 76BD207A
|
177
|
+
436C6481 F1D2B907 8717461A 5B9D32E6 88F87748 544523B5 24B0D57D
|
178
|
+
5EA77A27 75D2ECFA 032CFBDB F52FB378 61602790 04E57AE6 AF874E73
|
179
|
+
03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6
|
180
|
+
94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F
|
181
|
+
9E4AFF73
|
182
|
+
}.join.hex
|
183
|
+
@g = 2
|
184
|
+
|
185
|
+
when 3072
|
186
|
+
@N = %w{
|
187
|
+
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
|
188
|
+
8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
|
189
|
+
302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
|
190
|
+
A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
|
191
|
+
49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
|
192
|
+
FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
|
193
|
+
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
|
194
|
+
180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
|
195
|
+
3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
|
196
|
+
04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
|
197
|
+
B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
|
198
|
+
1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
|
199
|
+
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
|
200
|
+
E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF
|
201
|
+
}.join.hex
|
202
|
+
@g = 5
|
203
|
+
|
204
|
+
when 4096
|
205
|
+
@N = %w{
|
206
|
+
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
|
207
|
+
8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
|
208
|
+
302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
|
209
|
+
A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
|
210
|
+
49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
|
211
|
+
FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
|
212
|
+
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
|
213
|
+
180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
|
214
|
+
3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
|
215
|
+
04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
|
216
|
+
B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
|
217
|
+
1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
|
218
|
+
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
|
219
|
+
E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
|
220
|
+
99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
|
221
|
+
04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
|
222
|
+
233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
|
223
|
+
D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
|
224
|
+
FFFFFFFF FFFFFFFF
|
225
|
+
}.join.hex
|
226
|
+
@g = 5
|
227
|
+
|
228
|
+
when 6144
|
229
|
+
@N = %w{
|
230
|
+
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
|
231
|
+
8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
|
232
|
+
302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
|
233
|
+
A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
|
234
|
+
49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
|
235
|
+
FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
|
236
|
+
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
|
237
|
+
180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
|
238
|
+
3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
|
239
|
+
04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
|
240
|
+
B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
|
241
|
+
1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
|
242
|
+
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
|
243
|
+
E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
|
244
|
+
99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
|
245
|
+
04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
|
246
|
+
233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
|
247
|
+
D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
|
248
|
+
36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406
|
249
|
+
AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918
|
250
|
+
DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151
|
251
|
+
2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03
|
252
|
+
F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F
|
253
|
+
BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
|
254
|
+
CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
|
255
|
+
B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
|
256
|
+
387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
|
257
|
+
6DCC4024 FFFFFFFF FFFFFFFF
|
258
|
+
}.join.hex
|
259
|
+
@g = 5
|
260
|
+
|
261
|
+
when 8192
|
262
|
+
@N = %w{
|
263
|
+
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
|
264
|
+
8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
|
265
|
+
302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
|
266
|
+
A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
|
267
|
+
49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
|
268
|
+
FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
|
269
|
+
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
|
270
|
+
180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
|
271
|
+
3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
|
272
|
+
04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
|
273
|
+
B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
|
274
|
+
1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
|
275
|
+
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
|
276
|
+
E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
|
277
|
+
99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
|
278
|
+
04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
|
279
|
+
233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
|
280
|
+
D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
|
281
|
+
36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406
|
282
|
+
AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918
|
283
|
+
DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151
|
284
|
+
2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03
|
285
|
+
F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F
|
286
|
+
BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
|
287
|
+
CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
|
288
|
+
B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
|
289
|
+
387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
|
290
|
+
6DBE1159 74A3926F 12FEE5E4 38777CB6 A932DF8C D8BEC4D0 73B931BA
|
291
|
+
3BC832B6 8D9DD300 741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C
|
292
|
+
5AE4F568 3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9
|
293
|
+
22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B 4BCBC886
|
294
|
+
2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A 062B3CF5 B3A278A6
|
295
|
+
6D2A13F8 3F44F82D DF310EE0 74AB6A36 4597E899 A0255DC1 64F31CC5
|
296
|
+
0846851D F9AB4819 5DED7EA1 B1D510BD 7EE74D73 FAF36BC3 1ECFA268
|
297
|
+
359046F4 EB879F92 4009438B 481C6CD7 889A002E D5EE382B C9190DA6
|
298
|
+
FC026E47 9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71
|
299
|
+
60C980DD 98EDD3DF FFFFFFFF FFFFFFFF
|
300
|
+
}.join.hex
|
301
|
+
@g = 19
|
302
|
+
|
303
|
+
else
|
304
|
+
raise NotImplementedError
|
305
|
+
end
|
306
|
+
return [@N, @g]
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
class Verifier
|
312
|
+
attr_reader :N, :g, :k, :A, :B, :b, :S, :K, :M, :H_AMK
|
313
|
+
|
314
|
+
def initialize group=1024
|
315
|
+
# select modulus (N) and generator (g)
|
316
|
+
@N, @g = SRP.Ng group
|
317
|
+
@k = SRP.calc_k(@N, @g)
|
318
|
+
end
|
319
|
+
|
320
|
+
# Initial user creation for the persistance layer.
|
321
|
+
# Not part of the authentication process.
|
322
|
+
# Returns { <username>, <password verifier>, <salt> }
|
323
|
+
def generate_userauth username, password
|
324
|
+
@salt ||= random_salt
|
325
|
+
x = SRP.calc_x(username, password, @salt)
|
326
|
+
v = SRP.calc_v(x, @N, @g)
|
327
|
+
return {:username => username, :verifier => "%x" % v, :salt => @salt}
|
328
|
+
end
|
329
|
+
|
330
|
+
# Authentication phase 1 - create challenge.
|
331
|
+
# Returns Hash with challenge for client and proof to be stored on server.
|
332
|
+
# Parameters should be given in hex.
|
333
|
+
def get_challenge_and_proof username, xverifier, xsalt, xaa
|
334
|
+
# SRP-6a safety check
|
335
|
+
return false if (xaa.to_i(16) % @N) == 0
|
336
|
+
generate_B(xverifier)
|
337
|
+
return {
|
338
|
+
:challenge => {:B => @B, :salt => xsalt},
|
339
|
+
:proof => {:A => xaa, :B => @B, :b => "%x" % @b,
|
340
|
+
:I => username, :s => xsalt, :v => xverifier}
|
341
|
+
}
|
342
|
+
end
|
343
|
+
|
344
|
+
# returns H_AMK on success, None on failure
|
345
|
+
# User -> Host: M = H(H(N) xor H(g), H(I), s, A, B, K)
|
346
|
+
# Host -> User: H(A, M, K)
|
347
|
+
def verify_session proof, client_M
|
348
|
+
@A = proof[:A]
|
349
|
+
@B = proof[:B]
|
350
|
+
@b = proof[:b].to_i(16)
|
351
|
+
username = proof[:I]
|
352
|
+
xsalt = proof[:s]
|
353
|
+
v = proof[:v].to_i(16)
|
354
|
+
|
355
|
+
u = SRP.calc_u(@A, @B, @N)
|
356
|
+
# SRP-6a safety check
|
357
|
+
return false if u == 0
|
358
|
+
|
359
|
+
# calculate session key
|
360
|
+
@S = "%x" % SRP.calc_server_S(@A.to_i(16), @b, v, u, @N)
|
361
|
+
@K = SRP.sha1_hex(@S)
|
362
|
+
|
363
|
+
# calculate match
|
364
|
+
@M = "%x" % SRP.calc_M(username, xsalt, @A, @B, @K, @N, @g)
|
365
|
+
|
366
|
+
if @M == client_M
|
367
|
+
# authentication succeeded
|
368
|
+
@H_AMK = "%x" % SRP.calc_H_AMK(@A, @M, @K, @N, @g)
|
369
|
+
return @H_AMK
|
370
|
+
end
|
371
|
+
return false
|
372
|
+
end
|
373
|
+
|
374
|
+
def random_salt
|
375
|
+
"%x" % SRP.bigrand(10).hex
|
376
|
+
end
|
377
|
+
|
378
|
+
def random_bignum
|
379
|
+
SRP.bigrand(32).hex
|
380
|
+
end
|
381
|
+
|
382
|
+
# generates challenge
|
383
|
+
# input verifier in hex
|
384
|
+
def generate_B xverifier
|
385
|
+
v = xverifier.to_i(16)
|
386
|
+
@b ||= random_bignum
|
387
|
+
@B = "%x" % SRP.calc_B(@b, k, v, @N, @g)
|
388
|
+
end
|
389
|
+
end # Verifier
|
390
|
+
|
391
|
+
|
392
|
+
class Client
|
393
|
+
attr_reader :N, :g, :k, :a, :A, :S, :K, :M, :H_AMK
|
394
|
+
|
395
|
+
def initialize group=1024
|
396
|
+
# select modulus (N) and generator (g)
|
397
|
+
@N, @g = SRP.Ng group
|
398
|
+
@k = SRP.calc_k(@N, @g)
|
399
|
+
end
|
400
|
+
|
401
|
+
def start_authentication
|
402
|
+
generate_A
|
403
|
+
end
|
404
|
+
|
405
|
+
# Process initiated authentication challenge.
|
406
|
+
# Returns M if authentication is successful, false otherwise.
|
407
|
+
# Salt and B should be given in hex.
|
408
|
+
def process_challenge username, password, xsalt, xbb
|
409
|
+
bb = xbb.to_i(16)
|
410
|
+
# SRP-6a safety check
|
411
|
+
return false if (bb % @N) == 0
|
412
|
+
|
413
|
+
x = SRP.calc_x(username, password, xsalt)
|
414
|
+
u = SRP.calc_u(@A, xbb, @N)
|
415
|
+
|
416
|
+
# SRP-6a safety check
|
417
|
+
return false if u == 0
|
418
|
+
|
419
|
+
# calculate session key
|
420
|
+
@S = "%x" % SRP.calc_client_S(bb, @a, @k, x, u, @N, @g)
|
421
|
+
@K = SRP.sha1_hex(@S)
|
422
|
+
|
423
|
+
# calculate match
|
424
|
+
@M = "%x" % SRP.calc_M(username, xsalt, @A, xbb, @K, @N, @g)
|
425
|
+
|
426
|
+
# calculate verifier
|
427
|
+
@H_AMK = "%x" % SRP.calc_H_AMK(@A, @M, @K, @N, @g)
|
428
|
+
|
429
|
+
return @M
|
430
|
+
end
|
431
|
+
|
432
|
+
def verify server_HAMK
|
433
|
+
return false unless @H_AMK
|
434
|
+
@H_AMK == server_HAMK
|
435
|
+
end
|
436
|
+
|
437
|
+
def random_bignum
|
438
|
+
SRP.bigrand(32).hex
|
439
|
+
end
|
440
|
+
|
441
|
+
def generate_A
|
442
|
+
@a ||= random_bignum
|
443
|
+
@A = "%x" % SRP.calc_A(@a, @N, @g)
|
444
|
+
end
|
445
|
+
end # Client
|
446
|
+
end
|
data/lib/srp.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'srp-rb'
|
data/spec/srp_spec.rb
ADDED
@@ -0,0 +1,474 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
=begin
|
3
|
+
|
4
|
+
Copyright (c) 2012, Mikael Lammentausta
|
5
|
+
All rights reserved.
|
6
|
+
|
7
|
+
Following is the New BSD license:
|
8
|
+
|
9
|
+
Redistribution and use in source and binary forms, with or without
|
10
|
+
modification, are permitted provided that the following conditions are met:
|
11
|
+
* Redistributions of source code must retain the above copyright
|
12
|
+
notice, this list of conditions and the following disclaimer.
|
13
|
+
* Redistributions in binary form must reproduce the above copyright
|
14
|
+
notice, this list of conditions and the following disclaimer in the
|
15
|
+
documentation and/or other materials provided with the distribution.
|
16
|
+
* Neither the name of the Python Software Foundation nor the
|
17
|
+
names of its contributors may be used to endorse or promote products
|
18
|
+
derived from this software without specific prior written permission.
|
19
|
+
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
21
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
22
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL TOM COCAGNE BE LIABLE FOR ANY
|
24
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
25
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
26
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
27
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
28
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
=end
|
32
|
+
require 'srp'
|
33
|
+
|
34
|
+
# monkey-patch API to define a, b and salt presetters
|
35
|
+
class SRP::Verifier
|
36
|
+
def set_b val
|
37
|
+
@b = val
|
38
|
+
end
|
39
|
+
def set_salt val
|
40
|
+
@salt = val
|
41
|
+
end
|
42
|
+
end
|
43
|
+
class SRP::Client
|
44
|
+
def set_a val
|
45
|
+
@a = val
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe SRP do
|
50
|
+
###
|
51
|
+
### Test SRP functions.
|
52
|
+
### Values are from http://srp.stanford.edu/demo/demo.html
|
53
|
+
### using 256 bit values.
|
54
|
+
###
|
55
|
+
context "@module-functions" do
|
56
|
+
before :all do
|
57
|
+
@N = "115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3".to_i(16)
|
58
|
+
@g = 2
|
59
|
+
@username = "user"
|
60
|
+
@password = "password"
|
61
|
+
@salt = "16ccfa081895fe1ed0bb"
|
62
|
+
@a = "7ec87196e320a2f8dfe8979b1992e0d34439d24471b62c40564bb4302866e1c2".to_i(16)
|
63
|
+
@b = "8143e2f299852a05717427ea9d87c6146e747d0da6e95f4390264e55a43ae96".to_i(16)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should calculate k" do
|
67
|
+
k = SRP.calc_k(@N, @g)
|
68
|
+
("%x" % k).should == "dbe5dfe0704fee4c85ff106ecd38117d33bcfe50"
|
69
|
+
("%b" % k).length.should == 160
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should calculate x" do
|
73
|
+
x = SRP.calc_x(@username, @password, @salt)
|
74
|
+
("%x" % x).should == "bdd0a4e1c9df4082684d8d358b8016301b025375"
|
75
|
+
("%b" % x).length.should == 160
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should calculate verifier" do
|
79
|
+
x = "bdd0a4e1c9df4082684d8d358b8016301b025375".to_i(16)
|
80
|
+
v = SRP.calc_v(x, @N, @g)
|
81
|
+
("%x" % v).should == "ce36e101ed8c37ed98ba4e441274dabd1062f3440763eb98bd6058e5400b6309"
|
82
|
+
("%b" % v).length.should == 256
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should calculate u" do
|
86
|
+
aa = "b1c4827b0ce416953789db123051ed990023f43b396236b86e12a2c69638fb8e"
|
87
|
+
bb = "fbc56086bb51e26ee1a8287c0a7f3fd4e067e55beb8530b869b10b961957ff68"
|
88
|
+
u = SRP.calc_u(aa, bb, @N)
|
89
|
+
("%x" % u).should == "c60b17ddf568dd5743d0e3ba5621646b742432c5"
|
90
|
+
("%b" % u).length.should == 160
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should calculate public client value A" do
|
94
|
+
aa = SRP.calc_A(@a, @N, @g)
|
95
|
+
("%x" % aa).should == "b1c4827b0ce416953789db123051ed990023f43b396236b86e12a2c69638fb8e"
|
96
|
+
("%b" % aa).length.should == 256
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should calculate public server value B" do
|
100
|
+
k = "dbe5dfe0704fee4c85ff106ecd38117d33bcfe50".to_i(16)
|
101
|
+
v = "ce36e101ed8c37ed98ba4e441274dabd1062f3440763eb98bd6058e5400b6309".to_i(16)
|
102
|
+
bb = SRP.calc_B(@b, k, v, @N, @g)
|
103
|
+
("%x" % bb).should == "fbc56086bb51e26ee1a8287c0a7f3fd4e067e55beb8530b869b10b961957ff68"
|
104
|
+
("%b" % bb).length.should == 256
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should calculate session key from client params" do
|
108
|
+
bb = "fbc56086bb51e26ee1a8287c0a7f3fd4e067e55beb8530b869b10b961957ff68".to_i(16)
|
109
|
+
k = "dbe5dfe0704fee4c85ff106ecd38117d33bcfe50".to_i(16)
|
110
|
+
x = "bdd0a4e1c9df4082684d8d358b8016301b025375".to_i(16)
|
111
|
+
u = "c60b17ddf568dd5743d0e3ba5621646b742432c5".to_i(16)
|
112
|
+
a = @a
|
113
|
+
ss = SRP.calc_client_S(bb, a, k, x, u, @N, @g)
|
114
|
+
("%x" % ss).should == "a606c182e364d2c15f9cdbeeeb63bb00c831d1da65eedc1414f21157d0312a5a"
|
115
|
+
("%b" % ss).length.should == 256
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should calculate session key from server params" do
|
119
|
+
aa = "b1c4827b0ce416953789db123051ed990023f43b396236b86e12a2c69638fb8e".to_i(16)
|
120
|
+
v = "ce36e101ed8c37ed98ba4e441274dabd1062f3440763eb98bd6058e5400b6309".to_i(16)
|
121
|
+
u = "c60b17ddf568dd5743d0e3ba5621646b742432c5".to_i(16)
|
122
|
+
b = @b
|
123
|
+
ss = SRP.calc_server_S(aa, b, v, u, @N)
|
124
|
+
("%x" % ss).should == "a606c182e364d2c15f9cdbeeeb63bb00c831d1da65eedc1414f21157d0312a5a"
|
125
|
+
("%b" % ss).length.should == 256
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should calculate M" do
|
129
|
+
xaa = "b1c4827b0ce416953789db123051ed990023f43b396236b86e12a2c69638fb8e"
|
130
|
+
xbb = "fbc56086bb51e26ee1a8287c0a7f3fd4e067e55beb8530b869b10b961957ff68"
|
131
|
+
xss = "a606c182e364d2c15f9cdbeeeb63bb00c831d1da65eedc1414f21157d0312a5a"
|
132
|
+
xkk = SRP.sha1_hex(xss)
|
133
|
+
xkk.should == "5844898ea6e5f5d9b737bc0ba2fb9d5edd3f8e67"
|
134
|
+
mm = SRP.calc_M(@username, @salt, xaa, xbb, xkk, @N, @g)
|
135
|
+
("%x" % mm).should == "2da30b225850c17720ed483ae6d04bcb67e4448e"
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should calculate H(AMK)" do
|
139
|
+
xaa = "b1c4827b0ce416953789db123051ed990023f43b396236b86e12a2c69638fb8e"
|
140
|
+
xmm = "d597503056af882d5b27b419302ac7b2ea9d7468"
|
141
|
+
xkk = "5844898ea6e5f5d9b737bc0ba2fb9d5edd3f8e67"
|
142
|
+
h_amk = SRP.calc_H_AMK(xaa, xmm, xkk, @N, @g)
|
143
|
+
("%x" % h_amk).should == "ffc955a9227f1bf1d87d66bebecba081f54dbb7a"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
###
|
149
|
+
### Test predefined values for N and g.
|
150
|
+
### Values are from vectors listed in RFC 5054 Appendix B.
|
151
|
+
###
|
152
|
+
context "@predefined-Ng" do
|
153
|
+
it "should be 1024 bits" do
|
154
|
+
srp = SRP::Verifier.new(1024)
|
155
|
+
nn = srp.N
|
156
|
+
("%x" % nn).should == "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3"
|
157
|
+
("%b" % nn).length.should == 1024
|
158
|
+
srp.g.should == 2
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should be 1536 bits" do
|
162
|
+
srp = SRP::Verifier.new(1536)
|
163
|
+
nn = srp.N
|
164
|
+
("%b" % nn).length.should == 1536
|
165
|
+
srp.g.should == 2
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should be 2048 bits" do
|
169
|
+
srp = SRP::Verifier.new(2048)
|
170
|
+
nn = srp.N
|
171
|
+
("%b" % nn).length.should == 2048
|
172
|
+
srp.g.should == 2
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should be 3072 bits" do
|
176
|
+
srp = SRP::Verifier.new(3072)
|
177
|
+
nn = srp.N
|
178
|
+
("%b" % nn).length.should == 3072
|
179
|
+
srp.g.should == 5
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should be 4096 bits" do
|
183
|
+
srp = SRP::Verifier.new(4096)
|
184
|
+
nn = srp.N
|
185
|
+
("%b" % nn).length.should == 4096
|
186
|
+
srp.g.should == 5
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should be 6144 bits" do
|
190
|
+
srp = SRP::Verifier.new(6144)
|
191
|
+
nn = srp.N
|
192
|
+
("%b" % nn).length.should == 6144
|
193
|
+
srp.g.should == 5
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should be 8192 bits" do
|
197
|
+
srp = SRP::Verifier.new(8192)
|
198
|
+
nn = srp.N
|
199
|
+
("%b" % nn).length.should == 8192
|
200
|
+
srp.g.should == 19
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
###
|
206
|
+
### Test server-side Verifier.
|
207
|
+
### Values are from http://srp.stanford.edu/demo/demo.html
|
208
|
+
### using 1024 bit values.
|
209
|
+
###
|
210
|
+
context "@verifier" do
|
211
|
+
before :all do
|
212
|
+
@username = "user"
|
213
|
+
@password = "password"
|
214
|
+
@salt = "16ccfa081895fe1ed0bb"
|
215
|
+
@a = "7ec87196e320a2f8dfe8979b1992e0d34439d24471b62c40564bb4302866e1c2"
|
216
|
+
@b = "8143e2f299852a05717427ea9d87c6146e747d0da6e95f4390264e55a43ae96"
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should calculate k" do
|
220
|
+
k = SRP::Verifier.new(1024).k
|
221
|
+
k.should == "7556aa045aef2cdd07abaf0f665c3e818913186f".to_i(16)
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should generate salt and verifier" do
|
225
|
+
auth = SRP::Verifier.new(1024).generate_userauth(@username, @password)
|
226
|
+
auth[:username].should == @username
|
227
|
+
auth[:verifier].should be
|
228
|
+
auth[:salt].should be
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should calculate verifier with given salt" do
|
232
|
+
srp = SRP::Verifier.new(1024)
|
233
|
+
srp.set_salt @salt
|
234
|
+
auth = srp.generate_userauth(@username, @password)
|
235
|
+
v = auth[:verifier]
|
236
|
+
salt = auth[:salt]
|
237
|
+
salt.should == @salt
|
238
|
+
v.should == "321307d87ca3462f5b0cb5df295bea04498563794e5401899b2f32dd5cab5b7de9da78e7d62ea235e6d7f43a4ea09fea7c0dafdee6e79a1d12e2e374048deeaf5ba7c68e2ad952a3f5dc084400a7f1599a31d6d9d50269a9208db88f84090e8aa3c7b019f39529dcc19baa985a8d7ffb2d7628071d2313c9eaabc504d3333688"
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should generate salt and calculate verifier" do
|
242
|
+
srp = SRP::Verifier.new(1024)
|
243
|
+
auth = srp.generate_userauth(@username, @password)
|
244
|
+
v = auth[:verifier]
|
245
|
+
salt = auth[:salt]
|
246
|
+
("%b" % v.to_i(16)).length.should >= 1000
|
247
|
+
("%b" % salt.to_i(16)).length.should >= 50
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should generate B with predefined b" do
|
251
|
+
v = "321307d87ca3462f5b0cb5df295bea04498563794e5401899b2f32dd5cab5b7de9da78e7d62ea235e6d7f43a4ea09fea7c0dafdee6e79a1d12e2e374048deeaf5ba7c68e2ad952a3f5dc084400a7f1599a31d6d9d50269a9208db88f84090e8aa3c7b019f39529dcc19baa985a8d7ffb2d7628071d2313c9eaabc504d3333688"
|
252
|
+
srp = SRP::Verifier.new(1024)
|
253
|
+
srp.set_b @b.to_i(16)
|
254
|
+
bb = srp.generate_B(v)
|
255
|
+
bb.should == "56777d24af1121bd6af6aeb84238ff8d250122fe75ed251db0f47c289642ae7adb9ef319ce3ab23b6ecc97e5904749fc42f12bb016ecf39691db541f066667b8399bfa685c82b03ad8f92f75975ed086dbe0d470d4dd907ce11b19ee41b74aee72bd8445cde6b58c01f678e39ed9cd6b93c79382637df90777a96c10a768c510"
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should generate B" do
|
259
|
+
srp = SRP::Verifier.new(1024)
|
260
|
+
bb = srp.generate_B("0")
|
261
|
+
("%b" % bb.to_i(16)).length.should >= 1000
|
262
|
+
("%b" % srp.b).length.should > 200
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should calculate server session and key" do
|
266
|
+
# A is received in phase 1
|
267
|
+
aa = "165366e23a10006a62fb8a0793757a299e2985103ad2e8cdee0cc37cac109f3f338ee12e2440eda97bfa7c75697709a5dc66faadca7806d43ea5839757d134ae7b28dd3333049198cc8d328998b8cd8352ff4e64b3bd5f08e40148d69b0843bce18cbbb30c7e4760296da5c92717fcac8bddc7875f55302e55d90a34226868d2"
|
268
|
+
# B and b are saved from phase 1
|
269
|
+
bb = "56777d24af1121bd6af6aeb84238ff8d250122fe75ed251db0f47c289642ae7adb9ef319ce3ab23b6ecc97e5904749fc42f12bb016ecf39691db541f066667b8399bfa685c82b03ad8f92f75975ed086dbe0d470d4dd907ce11b19ee41b74aee72bd8445cde6b58c01f678e39ed9cd6b93c79382637df90777a96c10a768c510"
|
270
|
+
# v is from db
|
271
|
+
v = "321307d87ca3462f5b0cb5df295bea04498563794e5401899b2f32dd5cab5b7de9da78e7d62ea235e6d7f43a4ea09fea7c0dafdee6e79a1d12e2e374048deeaf5ba7c68e2ad952a3f5dc084400a7f1599a31d6d9d50269a9208db88f84090e8aa3c7b019f39529dcc19baa985a8d7ffb2d7628071d2313c9eaabc504d3333688"
|
272
|
+
_proof = {:A => aa, :B => bb, :b => @b,
|
273
|
+
:I => @username, :s => @salt, :v => v}
|
274
|
+
srp = SRP::Verifier.new(1024)
|
275
|
+
srp.verify_session(_proof, "match insignificant")
|
276
|
+
ss = srp.S
|
277
|
+
ss.should == "7f44592cc616e0d761b2d3309d513b69b386c35f3ed9b11e6d43f15799b673d6dcfa4117b4456af978458d62ad61e1a37be625f46d2a5bd9a50aae359e4541275f0f4bd4b4caed9d2da224b491231f905d47abd9953179aa608854b84a0e0c6195e73715932b41ab8d0d4a2977e7642163be6802c5907fb9e233b8c96e457314"
|
278
|
+
kk = srp.K
|
279
|
+
kk.should == "404bf923682abeeb3c8c9164d2cdb6b6ba21b64d"
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should calculate verifier M and server proof" do
|
283
|
+
# A is received in phase 1
|
284
|
+
aa = "165366e23a10006a62fb8a0793757a299e2985103ad2e8cdee0cc37cac109f3f338ee12e2440eda97bfa7c75697709a5dc66faadca7806d43ea5839757d134ae7b28dd3333049198cc8d328998b8cd8352ff4e64b3bd5f08e40148d69b0843bce18cbbb30c7e4760296da5c92717fcac8bddc7875f55302e55d90a34226868d2"
|
285
|
+
# B and b are saved from phase 1
|
286
|
+
bb = "56777d24af1121bd6af6aeb84238ff8d250122fe75ed251db0f47c289642ae7adb9ef319ce3ab23b6ecc97e5904749fc42f12bb016ecf39691db541f066667b8399bfa685c82b03ad8f92f75975ed086dbe0d470d4dd907ce11b19ee41b74aee72bd8445cde6b58c01f678e39ed9cd6b93c79382637df90777a96c10a768c510"
|
287
|
+
# v is from db
|
288
|
+
v = "321307d87ca3462f5b0cb5df295bea04498563794e5401899b2f32dd5cab5b7de9da78e7d62ea235e6d7f43a4ea09fea7c0dafdee6e79a1d12e2e374048deeaf5ba7c68e2ad952a3f5dc084400a7f1599a31d6d9d50269a9208db88f84090e8aa3c7b019f39529dcc19baa985a8d7ffb2d7628071d2313c9eaabc504d3333688"
|
289
|
+
# S is validated
|
290
|
+
ss = "7f44592cc616e0d761b2d3309d513b69b386c35f3ed9b11e6d43f15799b673d6dcfa4117b4456af978458d62ad61e1a37be625f46d2a5bd9a50aae359e4541275f0f4bd4b4caed9d2da224b491231f905d47abd9953179aa608854b84a0e0c6195e73715932b41ab8d0d4a2977e7642163be6802c5907fb9e233b8c96e457314"
|
291
|
+
# K = H(S)
|
292
|
+
kk = SRP.sha1_hex(ss)
|
293
|
+
client_M = "52fb39fcacc2d909675ea3cf2b967980fc40ae0"
|
294
|
+
_proof = {:A => aa, :B => bb, :b => @b,
|
295
|
+
:I => @username, :s => @salt, :v => v}
|
296
|
+
srp = SRP::Verifier.new(1024)
|
297
|
+
srp.verify_session(_proof, client_M)
|
298
|
+
srp.M.should == client_M
|
299
|
+
srp.H_AMK.should == "d3668cebb1cba4b3d4a4cd8edde9d89279b9d1e9"
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
###
|
305
|
+
### Test Client.
|
306
|
+
### Values are from http://srp.stanford.edu/demo/demo.html
|
307
|
+
### using 1024 bit values.
|
308
|
+
###
|
309
|
+
context "@client" do
|
310
|
+
before :all do
|
311
|
+
@username = "user"
|
312
|
+
@password = "password"
|
313
|
+
@salt = "16ccfa081895fe1ed0bb"
|
314
|
+
@a = "7ec87196e320a2f8dfe8979b1992e0d34439d24471b62c40564bb4302866e1c2"
|
315
|
+
@b = "8143e2f299852a05717427ea9d87c6146e747d0da6e95f4390264e55a43ae96"
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should generate A from random a" do
|
319
|
+
srp = SRP::Client.new(1024)
|
320
|
+
aa1 = srp.generate_A
|
321
|
+
("%b" % aa1.to_i(16)).length.should >= 1000
|
322
|
+
("%b" % srp.generate_A.to_i(16)).length.should >= 200
|
323
|
+
srp = SRP::Client.new(1024)
|
324
|
+
aa2 = srp.generate_A
|
325
|
+
("%b" % aa2.to_i(16)).length.should >= 1000
|
326
|
+
("%b" % srp.generate_A.to_i(16)).length.should >= 200
|
327
|
+
aa1.should_not == aa2
|
328
|
+
end
|
329
|
+
|
330
|
+
it "should calculate A" do
|
331
|
+
srp = SRP::Client.new(1024)
|
332
|
+
srp.set_a @a.to_i(16)
|
333
|
+
aa = srp.generate_A
|
334
|
+
aa.should == "165366e23a10006a62fb8a0793757a299e2985103ad2e8cdee0cc37cac109f3f338ee12e2440eda97bfa7c75697709a5dc66faadca7806d43ea5839757d134ae7b28dd3333049198cc8d328998b8cd8352ff4e64b3bd5f08e40148d69b0843bce18cbbb30c7e4760296da5c92717fcac8bddc7875f55302e55d90a34226868d2"
|
335
|
+
end
|
336
|
+
|
337
|
+
it "should calculate client session and key" do
|
338
|
+
srp = SRP::Client.new(1024)
|
339
|
+
srp.set_a @a.to_i(16)
|
340
|
+
aa = srp.generate_A # created in phase 1
|
341
|
+
bb = "56777d24af1121bd6af6aeb84238ff8d250122fe75ed251db0f47c289642ae7adb9ef319ce3ab23b6ecc97e5904749fc42f12bb016ecf39691db541f066667b8399bfa685c82b03ad8f92f75975ed086dbe0d470d4dd907ce11b19ee41b74aee72bd8445cde6b58c01f678e39ed9cd6b93c79382637df90777a96c10a768c510"
|
342
|
+
mm = srp.process_challenge(@username, @password, @salt, bb)
|
343
|
+
ss = srp.S
|
344
|
+
ss.should == "7f44592cc616e0d761b2d3309d513b69b386c35f3ed9b11e6d43f15799b673d6dcfa4117b4456af978458d62ad61e1a37be625f46d2a5bd9a50aae359e4541275f0f4bd4b4caed9d2da224b491231f905d47abd9953179aa608854b84a0e0c6195e73715932b41ab8d0d4a2977e7642163be6802c5907fb9e233b8c96e457314"
|
345
|
+
kk = srp.K
|
346
|
+
kk.should == "404bf923682abeeb3c8c9164d2cdb6b6ba21b64d"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
|
351
|
+
###
|
352
|
+
### Simulate actual authentication scenario over HTTP
|
353
|
+
### when the server is RESTful and has to persist authentication
|
354
|
+
### state between challenge and response.
|
355
|
+
###
|
356
|
+
context "@authentication" do
|
357
|
+
before :all do
|
358
|
+
@username = "leonardo"
|
359
|
+
password = "icnivad"
|
360
|
+
@auth = SRP::Verifier.new(1024).generate_userauth(@username, password)
|
361
|
+
# imitate database persistance layer
|
362
|
+
@db = {@username => {
|
363
|
+
:verifier => @auth[:verifier],
|
364
|
+
:salt => @auth[:salt],
|
365
|
+
}}
|
366
|
+
end
|
367
|
+
|
368
|
+
it "should authenticate" do
|
369
|
+
client = SRP::Client.new(1024)
|
370
|
+
verifier = SRP::Verifier.new(1024)
|
371
|
+
# phase 1
|
372
|
+
# (client)
|
373
|
+
aa = client.generate_A
|
374
|
+
# (server)
|
375
|
+
v = @auth[:verifier]
|
376
|
+
salt = @auth[:salt]
|
377
|
+
bb = verifier.generate_B v
|
378
|
+
b = "%x" % verifier.b
|
379
|
+
# phase 2
|
380
|
+
# (client)
|
381
|
+
client_M = client.process_challenge(@username, "icnivad", salt, bb)
|
382
|
+
# (server)
|
383
|
+
_proof = {:A => aa, :B => bb, :b => b, :I => @username, :s => salt, :v => v}
|
384
|
+
server_H_AMK = verifier.verify_session(_proof, client_M)
|
385
|
+
server_H_AMK.should be
|
386
|
+
# (client)
|
387
|
+
client.H_AMK.should == server_H_AMK
|
388
|
+
end
|
389
|
+
|
390
|
+
it "should not authenticate" do
|
391
|
+
client = SRP::Client.new(1024)
|
392
|
+
verifier = SRP::Verifier.new(1024)
|
393
|
+
# phase 1
|
394
|
+
# (client)
|
395
|
+
aa = client.generate_A
|
396
|
+
# (server)
|
397
|
+
v = @auth[:verifier]
|
398
|
+
salt = @auth[:salt]
|
399
|
+
bb = verifier.generate_B v
|
400
|
+
b = "%x" % verifier.b
|
401
|
+
# phase 2
|
402
|
+
# (client)
|
403
|
+
client_M = client.process_challenge(@username, "wrong password", salt, bb)
|
404
|
+
# (server)
|
405
|
+
_proof = {:A => aa, :B => bb, :b => b, :I => @username, :s => salt, :v => v}
|
406
|
+
verifier.verify_session(_proof, client_M).should == false
|
407
|
+
verifier.H_AMK.should_not be
|
408
|
+
end
|
409
|
+
|
410
|
+
it "should be applied in async authentication with stateless server" do
|
411
|
+
username = @username
|
412
|
+
|
413
|
+
# client generates A and begins authentication process
|
414
|
+
client = SRP::Client.new(1024)
|
415
|
+
aa = client.start_authentication()
|
416
|
+
|
417
|
+
|
418
|
+
#
|
419
|
+
# username and A are received (client --> server)
|
420
|
+
#
|
421
|
+
|
422
|
+
# server finds user from "database"
|
423
|
+
_user = @db[username]
|
424
|
+
_user.should_not be_nil
|
425
|
+
v = _user[:verifier]
|
426
|
+
salt = _user[:salt]
|
427
|
+
|
428
|
+
# server generates B, saves A and B to database
|
429
|
+
srp = SRP::Verifier.new(1024)
|
430
|
+
_session = srp.get_challenge_and_proof username, v, salt, aa
|
431
|
+
_session[:challenge][:B].should == srp.B
|
432
|
+
_session[:challenge][:salt].should == salt
|
433
|
+
# store proof to memory
|
434
|
+
_user[:session_proof] = _session[:proof]
|
435
|
+
# clear variables to simulate end of phase 1
|
436
|
+
srp = username = v = bb = salt = nil
|
437
|
+
# server sends salt and B
|
438
|
+
client_response = _session[:challenge]
|
439
|
+
|
440
|
+
|
441
|
+
#
|
442
|
+
# client receives B and salt (server --> client)
|
443
|
+
#
|
444
|
+
bb = client_response[:B]
|
445
|
+
salt = client_response[:salt]
|
446
|
+
# client generates session key
|
447
|
+
# at this point _client_srp.a should be persisted!! calculate_client_key is stateful!
|
448
|
+
mmc = client.process_challenge @username, "icnivad", salt, bb
|
449
|
+
client.A.should be
|
450
|
+
client.M.should == mmc
|
451
|
+
client.K.should be
|
452
|
+
client.H_AMK.should be
|
453
|
+
# client sends M --> server
|
454
|
+
client_M = client.M
|
455
|
+
|
456
|
+
#
|
457
|
+
# server receives client session key (client --> server)
|
458
|
+
#
|
459
|
+
username = @username
|
460
|
+
_user = @db[username]
|
461
|
+
# retrive session from database
|
462
|
+
proof = _user[:session_proof]
|
463
|
+
srp = SRP::Verifier.new(1024)
|
464
|
+
verification = srp.verify_session(proof, client_M)
|
465
|
+
verification.should_not == false
|
466
|
+
|
467
|
+
# Now the two parties have a shared, strong session key K.
|
468
|
+
# To complete authentication, they need to prove to each other that their keys match.
|
469
|
+
|
470
|
+
client.verify(verification).should == true
|
471
|
+
verification.should == client.H_AMK
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: srp-rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- lamikae
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: ! "\n Ruby implementation of the Secure Remote Password protocol (SRP-6a).\n
|
31
|
+
\ SRP is a cryptographically strong authentication protocol for \n password-based,
|
32
|
+
mutual authentication over an insecure network connection."
|
33
|
+
email:
|
34
|
+
- ''
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files: []
|
38
|
+
files:
|
39
|
+
- lib/srp-rb/version.rb
|
40
|
+
- lib/srp-rb.rb
|
41
|
+
- lib/srp.rb
|
42
|
+
- spec/srp_spec.rb
|
43
|
+
homepage: ''
|
44
|
+
licenses: []
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubyforge_project: srp-rb
|
63
|
+
rubygems_version: 1.8.25
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Secure Remote Password protocol SRP-6a.
|
67
|
+
test_files:
|
68
|
+
- spec/srp_spec.rb
|