diff options
Diffstat (limited to '_pysrp.py')
-rw-r--r-- | _pysrp.py | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/_pysrp.py b/_pysrp.py new file mode 100644 index 0000000..8a89e71 --- /dev/null +++ b/_pysrp.py @@ -0,0 +1,225 @@ + # N A large safe prime (N = 2q+1, where q is prime) + # All arithmetic is done modulo N. + # g A generator modulo N + # k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + # s User's salt + # I Username + # p Cleartext Password + # H() One-way hash function + # ^ (Modular) Exponentiation + # u Random scrambling parameter + # a,b Secret ephemeral values + # A,B Public ephemeral values + # x Private key (derived from p and s) + # v Password verifier + +import hashlib +import os +import binascii + + +def bytes_to_long(s): + n = ord(s[0]) + for b in ( ord(x) for x in s[1:] ): + n = (n << 8) | b + return n + + +def long_to_bytes(n): + l = list() + x = 0 + off = 0 + while x != n: + b = (n >> off) & 0xFF + l.append( chr(b) ) + x = x | (b << off) + off += 8 + l.reverse() + return ''.join(l) + + +def get_random( nbytes ): + return bytes_to_long( os.urandom( nbytes ) ) + + +def old_H( s1, s2 = '', s3=''): + if isinstance(s1, (long, int)): + s1 = long_to_bytes(s1) + if s2 and isinstance(s2, (long, int)): + s2 = long_to_bytes(s2) + if s3 and isinstance(s3, (long, int)): + s3 = long_to_bytes(s3) + s = s1 + s2 + s3 + return long(hashlib.sha256(s).hexdigest(), 16) + + +def H( *args, **kwargs ): + h = hashlib.sha256() + + for s in args: + if s is not None: + h.update( long_to_bytes(s) if isinstance(s, (long, int)) else s ) + + return long( h.hexdigest(), 16 ) + + + +N = 0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73; +g = 2; +k = H(N,g) + +hN = hashlib.sha256( long_to_bytes(N) ).digest() +hg = hashlib.sha256( long_to_bytes(g) ).digest() + +HNxorg = ''.join( chr( ord(hN[i]) ^ ord(hg[i]) ) for i in range(0,len(hN)) ) + + + +def gen_x( salt, username, password ): + return H( salt, H( username + ':' + password ) ) + + + + +def gen_sv( username, password ): + _s = long_to_bytes( get_random( 4 ) ) + _v = long_to_bytes( pow(g, gen_x( _s, username, password ), N) ) + + return _s, _v + + + +def calculate_M( I, s, A, B, K ): + h = hashlib.sha256() + h.update( HNxorg ) + h.update( hashlib.sha256(I).digest() ) + h.update( long_to_bytes(s) ) + h.update( long_to_bytes(A) ) + h.update( long_to_bytes(B) ) + h.update( K ) + return h.digest() + + +def calculate_H_AMK( A, M, K ): + h = hashlib.sha256() + h.update( long_to_bytes(A) ) + h.update( M ) + h.update( K ) + return h.digest() + + + + +class Verifier (object): + + def __init__(self, username, bytes_s, bytes_v, bytes_A): + self.s = bytes_to_long(bytes_s) + self.v = bytes_to_long(bytes_v) + self.I = username + self.K = None + self._authenticated = False + + self.A = bytes_to_long(bytes_A) + + # SRP-6a safety check + self.safety_failed = self.A % N == 0 + + if not self.safety_failed: + + self.b = get_random( 32 ) + self.B = (k*self.v + pow(g, self.b, N)) % N + self.u = H(self.A, self.B) + self.S = pow(self.A*pow(self.v, self.u, N ), self.b, N) + self.K = hashlib.sha256( long_to_bytes(self.S) ).digest() + self.M = calculate_M( self.I, self.s, self.A, self.B, self.K ) + self.H_AMK = calculate_H_AMK(self.A, self.M, self.K) + + + def authenticated(self): + return self._authenticated + + + def get_username(self): + return self.I + + + def get_session_key(self): + return self.K if self._authenticated else None + + # returns (bytes_s, bytes_B) on success, (None,None) if SRP-6a safety check fails + def get_challenge(self): + if self.safety_failed: + return None,None + else: + return (long_to_bytes(self.s), long_to_bytes(self.B)) + + # returns H_AMK on success, None on failure + def verify_session(self, user_M): + if not self.safety_failed and user_M == self.M: + self._authenticated = True + return self.H_AMK + + + + +class User (object): + def __init__(self, username, password): + self.I = username + self.p = password + self.a = get_random( 32 ) + self.A = pow(g, self.a, N) + self.v = None + self.M = None + self.K = None + self.H_AMK = None + self._authenticated = False + + + def authenticated(self): + return self._authenticated + + + def get_username(self): + return self.username + + + def get_session_key(self): + return self.K if self._authenticated else None + + + def start_authentication(self): + return (self.I, long_to_bytes(self.A)) + + + # Returns M or None if SRP-6a safety check is violated + def process_challenge(self, bytes_s, bytes_B): + + self.s = bytes_to_long( bytes_s ) + self.B = bytes_to_long( bytes_B ) + + # SRP-6a safety check + if (self.B % N) == 0: + return None + + self.u = H( self.A, self.B ) + + # SRP-6a safety check + if self.u == 0: + return None + + self.x = gen_x( self.s, self.I, self.p ) + + self.v = pow(g, self.x, N) + + self.S = pow((self.B - k*self.v), (self.a + self.u*self.x), N) + + self.K = hashlib.sha256( long_to_bytes(self.S) ).digest() + self.M = calculate_M( self.I, self.s, self.A, self.B, self.K ) + self.H_AMK = calculate_H_AMK(self.A, self.M, self.K) + + return self.M + + + def verify_session(self, host_HAMK): + if self.H_AMK == host_HAMK: + self._authenticated = True |