Mercurial > ~darius > hgwebdir.cgi > adslstats
annotate mysrp.py @ 37:4f9a79f733ff
Don't explode when the link is down.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Sat, 21 Nov 2020 10:31:42 +1030 |
parents | 1af6865189ce |
children |
rev | line source |
---|---|
22 | 1 # Modified version of https://github.com/cocagne/pysrp/blob/master/srp/_pysrp.py |
2 | |
3 # N A large safe prime (N = 2q+1, where q is prime) | |
4 # All arithmetic is done modulo N. | |
5 # g A generator modulo N | |
6 # k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) | |
7 # s User's salt | |
8 # I Username | |
9 # p Cleartext Password | |
10 # H() One-way hash function | |
11 # ^ (Modular) Exponentiation | |
12 # u Random scrambling parameter | |
13 # a,b Secret ephemeral values | |
14 # A,B Public ephemeral values | |
15 # x Private key (derived from p and s) | |
16 # v Password verifier | |
17 | |
18 import hashlib | |
19 import os | |
20 import binascii | |
21 import six | |
22 | |
23 SHA1 = 0 | |
24 SHA224 = 1 | |
25 SHA256 = 2 | |
26 SHA384 = 3 | |
27 SHA512 = 4 | |
28 | |
29 NG_1024 = 0 | |
30 NG_2048 = 1 | |
31 NG_4096 = 2 | |
32 NG_8192 = 3 | |
33 NG_CUSTOM = 4 | |
34 | |
35 _hash_map = { SHA1 : hashlib.sha1, | |
36 SHA224 : hashlib.sha224, | |
37 SHA256 : hashlib.sha256, | |
38 SHA384 : hashlib.sha384, | |
39 SHA512 : hashlib.sha512 } | |
40 | |
41 | |
42 _ng_const = ( | |
43 # 1024-bit | |
44 ('''\ | |
45 EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496\ | |
46 EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E\ | |
47 F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA\ | |
48 9AFD5138FE8376435B9FC61D2FC0EB06E3''', | |
49 "2"), | |
50 # 2048 | |
51 ('''\ | |
52 AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4\ | |
53 A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60\ | |
54 95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF\ | |
55 747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907\ | |
56 8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861\ | |
57 60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB\ | |
58 FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73''', | |
59 "2"), | |
60 # 4096 | |
61 ('''\ | |
62 FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ | |
63 8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ | |
64 302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ | |
65 A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ | |
66 49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ | |
67 FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ | |
68 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ | |
69 180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ | |
70 3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ | |
71 04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ | |
72 B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ | |
73 1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ | |
74 BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ | |
75 E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ | |
76 99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ | |
77 04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ | |
78 233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ | |
79 D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199\ | |
80 FFFFFFFFFFFFFFFF''', | |
81 "5"), | |
82 # 8192 | |
83 ('''\ | |
84 FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ | |
85 8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ | |
86 302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ | |
87 A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ | |
88 49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ | |
89 FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ | |
90 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ | |
91 180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ | |
92 3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ | |
93 04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ | |
94 B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ | |
95 1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ | |
96 BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ | |
97 E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ | |
98 99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ | |
99 04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ | |
100 233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ | |
101 D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492\ | |
102 36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406\ | |
103 AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918\ | |
104 DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151\ | |
105 2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03\ | |
106 F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F\ | |
107 BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA\ | |
108 CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B\ | |
109 B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632\ | |
110 387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E\ | |
111 6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA\ | |
112 3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C\ | |
113 5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9\ | |
114 22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886\ | |
115 2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6\ | |
116 6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5\ | |
117 0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268\ | |
118 359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6\ | |
119 FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71\ | |
120 60C980DD98EDD3DFFFFFFFFFFFFFFFFF''', | |
121 '0x13') | |
122 ) | |
123 | |
124 def get_ng( ng_type, n_hex, g_hex ): | |
125 if ng_type < NG_CUSTOM: | |
126 n_hex, g_hex = _ng_const[ ng_type ] | |
127 return int(n_hex,16), int(g_hex,16) | |
128 | |
129 | |
130 def bytes_to_long(s): | |
131 n = 0 | |
132 for b in six.iterbytes(s): | |
133 n = (n << 8) | b | |
134 return n | |
135 | |
136 | |
137 def long_to_bytes(n): | |
138 l = list() | |
139 x = 0 | |
140 off = 0 | |
141 while x != n: | |
142 b = (n >> off) & 0xFF | |
143 l.append( chr(b) ) | |
144 x = x | (b << off) | |
145 off += 8 | |
146 l.reverse() | |
147 return six.b(''.join(l)) | |
148 | |
149 | |
150 def get_random( nbytes ): | |
151 return bytes_to_long( os.urandom( nbytes ) ) | |
152 | |
153 | |
154 def get_random_of_length( nbytes ): | |
155 offset = (nbytes*8) - 1 | |
156 return get_random( nbytes ) | (1 << offset) | |
157 | |
158 | |
159 def old_H( hash_class, s1, s2 = '', s3=''): | |
160 if isinstance(s1, six.integer_types): | |
161 s1 = long_to_bytes(s1) | |
162 if s2 and isinstance(s2, six.integer_types): | |
163 s2 = long_to_bytes(s2) | |
164 if s3 and isinstance(s3, six.integer_types): | |
165 s3 = long_to_bytes(s3) | |
166 s = s1 + s2 + s3 | |
32
1af6865189ce
Update to work with Python 3.
Daniel O'Connor <darius@dons.net.au>
parents:
22
diff
changeset
|
167 return int(hash_class(s).hexdigest(), 16) |
22 | 168 |
169 | |
170 def H( hash_class, *args, **kwargs ): | |
171 h = hash_class() | |
172 | |
173 for s in args: | |
174 if s is not None: | |
175 h.update( long_to_bytes(s) if isinstance(s, six.integer_types) else s ) | |
176 | |
177 return int( h.hexdigest(), 16 ) | |
178 | |
179 | |
180 | |
181 #N = 0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73; | |
182 #g = 2; | |
183 #k = H(N,g) | |
184 | |
185 def HNxorg( hash_class, N, g ): | |
186 hN = hash_class( long_to_bytes(N) ).digest() | |
187 hg = hash_class( long_to_bytes(g) ).digest() | |
188 | |
189 return six.b( ''.join( chr( six.indexbytes(hN, i) ^ six.indexbytes(hg, i) ) for i in range(0,len(hN)) ) ) | |
190 | |
191 | |
192 | |
193 def gen_x( hash_class, salt, username, password ): | |
194 return H( hash_class, salt, H( hash_class, username + six.b(':') + password ) ) | |
195 | |
196 | |
197 | |
198 | |
199 def create_salted_verification_key( username, password, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None ): | |
200 if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): | |
201 raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") | |
202 hash_class = _hash_map[ hash_alg ] | |
203 N,g = get_ng( ng_type, n_hex, g_hex ) | |
204 _s = long_to_bytes( get_random( 4 ) ) | |
205 _v = long_to_bytes( pow(g, gen_x( hash_class, _s, username, password ), N) ) | |
206 | |
207 return _s, _v | |
208 | |
209 | |
210 | |
211 def calculate_M( hash_class, N, g, I, s, A, B, K ): | |
212 h = hash_class() | |
213 h.update( HNxorg( hash_class, N, g ) ) | |
214 h.update( hash_class(I).digest() ) | |
215 h.update( long_to_bytes(s) ) | |
216 h.update( long_to_bytes(A) ) | |
217 h.update( long_to_bytes(B) ) | |
218 h.update( K ) | |
219 return h.digest() | |
220 | |
221 | |
222 def calculate_H_AMK( hash_class, A, M, K ): | |
223 h = hash_class() | |
224 h.update( long_to_bytes(A) ) | |
225 h.update( M ) | |
226 h.update( K ) | |
227 return h.digest() | |
228 | |
229 | |
230 | |
231 class User (object): | |
232 def __init__(self, username, password, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None, bytes_a=None): | |
233 if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): | |
234 raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") | |
235 if bytes_a and len(bytes_a) != 32: | |
236 raise ValueError("32 bytes required for bytes_a") | |
237 N,g = get_ng( ng_type, n_hex, g_hex ) | |
238 hash_class = _hash_map[ hash_alg ] | |
239 #k = H( hash_class, N, g ) | |
240 # XXX: modified to be SRP-6 as that is what the router uses | |
241 k = int('05b9e8ef059c6b32ea59fc1d322d37f04aa30bae5aa9003b8321e21ddb04e300', 16) | |
242 | |
243 self.I = username | |
244 self.p = password | |
245 if bytes_a: | |
246 self.a = bytes_to_long(bytes_a) | |
247 else: | |
248 self.a = get_random_of_length( 32 ) | |
249 self.A = pow(g, self.a, N) | |
250 self.v = None | |
251 self.M = None | |
252 self.K = None | |
253 self.H_AMK = None | |
254 self._authenticated = False | |
255 | |
256 self.hash_class = hash_class | |
257 self.N = N | |
258 self.g = g | |
259 self.k = k | |
260 | |
261 | |
262 def authenticated(self): | |
263 return self._authenticated | |
264 | |
265 | |
266 def get_username(self): | |
267 return self.I | |
268 | |
269 | |
270 def get_ephemeral_secret(self): | |
271 return long_to_bytes(self.a) | |
272 | |
273 | |
274 def get_session_key(self): | |
275 return self.K if self._authenticated else None | |
276 | |
277 | |
278 def start_authentication(self): | |
279 return (self.I, long_to_bytes(self.A)) | |
280 | |
281 | |
282 # Returns M or None if SRP-6a safety check is violated | |
283 def process_challenge(self, bytes_s, bytes_B): | |
284 | |
285 self.s = bytes_to_long( bytes_s ) | |
286 self.B = bytes_to_long( bytes_B ) | |
287 | |
288 N = self.N | |
289 g = self.g | |
290 k = self.k | |
291 | |
292 hash_class = self.hash_class | |
293 | |
294 # SRP-6a safety check | |
295 if (self.B % N) == 0: | |
296 return None | |
297 | |
298 self.u = H( hash_class, self.A, self.B ) | |
299 | |
300 # SRP-6a safety check | |
301 if self.u == 0: | |
302 return None | |
303 | |
304 self.x = gen_x( hash_class, self.s, self.I, self.p ) | |
305 | |
306 self.v = pow(g, self.x, N) | |
307 | |
308 self.S = pow((self.B - k*self.v), (self.a + self.u*self.x), N) | |
309 | |
310 self.K = hash_class( long_to_bytes(self.S) ).digest() | |
311 self.M = calculate_M( hash_class, N, g, self.I, self.s, self.A, self.B, self.K ) | |
312 self.H_AMK = calculate_H_AMK(hash_class, self.A, self.M, self.K) | |
313 | |
314 return self.M | |
315 | |
316 | |
317 def verify_session(self, host_HAMK): | |
318 if self.H_AMK == host_HAMK: | |
319 self._authenticated = True |