pax_global_header00006660000000000000000000000064122104313670014511gustar00rootroot0000000000000052 comment=2cbdceca4452673df9ccf0ea96246312134ee02f pysrp-1.0.4/000077500000000000000000000000001221043136700126705ustar00rootroot00000000000000pysrp-1.0.4/.hgignore000066400000000000000000000005401221043136700144720ustar00rootroot00000000000000syntax: re # This line is a comment, and will be skipped. # Empty lines are skipped too. # Backup files left behind by the Emacs editor. /*~ \.pyc$ # Lock files used by the Emacs editor. # Notice that the "#" character is quoted with a backslash. # This prevents it from being interpreted as starting a comment. .*\#.* \.so$ \.o$ .*old_.* \.orig$ pysrp-1.0.4/LICENSE000066400000000000000000000020661221043136700137010ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2012 Tom Cocagne Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pysrp-1.0.4/MANIFEST.in000066400000000000000000000001041221043136700144210ustar00rootroot00000000000000include *.txt recursive-include srp *.py recursive-include srp *.rstpysrp-1.0.4/README.md000066400000000000000000000066671221043136700141660ustar00rootroot00000000000000pysrp ===== Tom Cocagne <tom.cocagne@gmail.com> pysrp provides a Python implementation of the [Secure Remote Password protocol](http://srp.stanford.edu/) (SRP). SRP Overview ------------ SRP is a cryptographically strong authentication protocol for password-based, mutual authentication over an insecure network connection. Unlike other common challenge-response autentication protocols, such as Kereros and SSL, SRP does not rely on an external infrastructure of trusted key servers or certificate management. Instead, SRP server applications use verification keys derived from each user's password to determine the authenticity of a network connection. SRP provides mutual-authentication in that successful authentication requires both sides of the connection to have knowledge of the user's password. If the client side lacks the user's password or the server side lacks the proper verification key, the authentication will fail. Unlike SSL, SRP does not directly encrypt all data flowing through the authenticated connection. However, successful authentication does result in a cryptographically strong shared key that can be used for symmetric-key encryption. For a full description of the pysrp package and the SRP protocol, please refer to the [pysrp documentation](http://pythonhosted.org/srp/) Usage Example ------------- ```python import srp # The salt and verifier returned from srp.create_salted_verification_key() should be # stored on the server. salt, vkey = srp.create_salted_verification_key( 'testuser', 'testpassword' ) class AuthenticationFailed (Exception): pass # ~~~ Begin Authentication ~~~ usr = srp.User( 'testuser', 'testpassword' ) uname, A = usr.start_authentication() # The authentication process can fail at each step from this # point on. To comply with the SRP protocol, the authentication # process should be aborted on the first failure. # Client => Server: username, A svr = srp.Verifier( uname, salt, vkey, A ) s,B = svr.get_challenge() if s is None or B is None: raise AuthenticationFailed() # Server => Client: s, B M = usr.process_challenge( s, B ) if M is None: raise AuthenticationFailed() # Client => Server: M HAMK = svr.verify_session( M ) if HAMK is None: raise AuthenticationFailed() # Server => Client: HAMK usr.verify_session( HAMK ) # At this point the authentication process is complete. assert usr.authenticated() assert svr.authenticated() ``` Installation ------------ ``` $ pip install srp ``` Implementation -------------- It consists of 3 modules: A pure Python implementation, A ctypes + OpenSSL implementation, and a C extension module. The ctypes & extension modules are approximately 10-20x faster than the pure Python implementation and can take advantage of multiple CPUs. The extension module will be used if available, otherwise the library will fall back to the ctypes implementation followed by the pure Python implementation. Note: The test_srp.py script prints the performance timings for each combination of hash algorithm and prime number size. This may be of use in deciding which pair of parameters to use in the unlikely event that the defaults are unacceptable. Installation from source: ``` $ python setup.py install ``` Documentation: ``` $ cd srp/doc $ sphinx-build -b html . ``` Validity & Performance Testing: ``` $ python setup.py build $ python test_srp.py ``` pysrp-1.0.4/setup.py000077500000000000000000000045651221043136700144170ustar00rootroot00000000000000#!/usr/bin/env python from distutils.core import setup from distutils.extension import Extension long_description = ''' This package provides an implementation of the Secure Remote Password protocol (SRP). SRP is a cryptographically strong authentication protocol for password-based, mutual authentication over an insecure network connection. Unlike other common challenge-response autentication protocols, such as Kereros and SSL, SRP does not rely on an external infrastructure of trusted key servers or certificate management. Instead, SRP server applications use verification keys derived from each user's password to determine the authenticity of a network connection. SRP provides mutual-authentication in that successful authentication requires both sides of the connection to have knowledge of the user's password. If the client side lacks the user's password or the server side lacks the proper verification key, the authentication will fail. Unlike SSL, SRP does not directly encrypt all data flowing through the authenticated connection. However, successful authentication does result in a cryptographically strong shared key that can be used for symmetric-key encryption. For a full description of the pysrp package and the SRP protocol, please refer to the `srp module documentation`_. .. _`srp module documentation`: http://packages.python.org/srp ''' ext_modules = [ Extension('srp._srp', ['srp/_srp.c',], libraries = ['ssl',]), ] setup(name = 'srp', version = '1.0.4', description = 'Secure Remote Password', author = 'Tom Cocagne', author_email = 'tom.cocagne@gmail.com', url = 'http://code.google.com/p/pysrp/', download_url = 'http://pypi.python.org/pypi/srp', long_description = long_description, provides = ['srp'], packages = ['srp'], package_data = {'srp' : ['doc/*.rst', 'doc/*.py']}, ext_modules = ext_modules, license = "New BSD", platforms = "OS Independent", classifiers = [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: C', 'Topic :: Security', ],) pysrp-1.0.4/srp/000077500000000000000000000000001221043136700134745ustar00rootroot00000000000000pysrp-1.0.4/srp/__init__.py000066400000000000000000000013641221043136700156110ustar00rootroot00000000000000 _mod = None try: import srp._srp _mod = srp._srp except ImportError: pass if not _mod: try: import srp._ctsrp _mod = srp._ctsrp except ImportError: pass if not _mod: import srp._pysrp _mod = srp._pysrp User = _mod.User Verifier = _mod.Verifier create_salted_verification_key = _mod.create_salted_verification_key SHA1 = _mod.SHA1 SHA224 = _mod.SHA224 SHA256 = _mod.SHA256 SHA384 = _mod.SHA384 SHA512 = _mod.SHA512 NG_1024 = _mod.NG_1024 NG_2048 = _mod.NG_2048 NG_4096 = _mod.NG_4096 NG_8192 = _mod.NG_8192 NG_CUSTOM = _mod.NG_CUSTOM pysrp-1.0.4/srp/_ctsrp.py000066400000000000000000000451411221043136700153450ustar00rootroot00000000000000 # 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 os import sys import hashlib import random import ctypes import time SHA1 = 0 SHA224 = 1 SHA256 = 2 SHA384 = 3 SHA512 = 4 NG_1024 = 0 NG_2048 = 1 NG_4096 = 2 NG_8192 = 3 NG_CUSTOM = 4 _hash_map = { SHA1 : hashlib.sha1, SHA224 : hashlib.sha224, SHA256 : hashlib.sha256, SHA384 : hashlib.sha384, SHA512 : hashlib.sha512 } _ng_const = ( # 1024-bit ('''\ EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496\ EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E\ F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA\ 9AFD5138FE8376435B9FC61D2FC0EB06E3''', "2"), # 2048 ('''\ AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4\ A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60\ 95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF\ 747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907\ 8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861\ 60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB\ FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73''', "2"), # 4096 ('''\ FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ 8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ 302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ 49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ 180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ 3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ 04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ 1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ 99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ 04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ 233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199\ FFFFFFFFFFFFFFFF''', "5"), # 8192 ('''\ FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ 8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ 302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ 49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ 180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ 3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ 04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ 1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ 99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ 04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ 233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492\ 36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406\ AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918\ DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151\ 2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03\ F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F\ BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA\ CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B\ B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632\ 387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E\ 6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA\ 3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C\ 5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9\ 22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886\ 2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6\ 6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5\ 0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268\ 359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6\ FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71\ 60C980DD98EDD3DFFFFFFFFFFFFFFFFF''', '13') ) #N_HEX = "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73" #G_HEX = "2" #HNxorg = None dlls = list() if 'win' in sys.platform: for d in ('libeay32.dll', 'libssl32.dll', 'ssleay32.dll'): try: dlls.append( ctypes.cdll.LoadLibrary(d) ) except: pass else: dlls.append( ctypes.cdll.LoadLibrary('libssl.so') ) class BIGNUM_Struct (ctypes.Structure): _fields_ = [ ("d", ctypes.c_void_p), ("top", ctypes.c_int), ("dmax", ctypes.c_int), ("neg", ctypes.c_int), ("flags", ctypes.c_int) ] class BN_CTX_Struct (ctypes.Structure): _fields_ = [ ("_", ctypes.c_byte) ] BIGNUM = ctypes.POINTER( BIGNUM_Struct ) BN_CTX = ctypes.POINTER( BN_CTX_Struct ) def load_func( name, args, returns = ctypes.c_int): d = sys.modules[ __name__ ].__dict__ f = None for dll in dlls: try: f = getattr(dll, name) f.argtypes = args f.restype = returns d[ name ] = f return except: pass raise ImportError('Unable to load required functions from SSL dlls') load_func( 'BN_new', [], BIGNUM ) load_func( 'BN_free', [ BIGNUM ], None ) load_func( 'BN_init', [ BIGNUM ], None ) load_func( 'BN_clear', [ BIGNUM ], None ) load_func( 'BN_CTX_new', [] , BN_CTX ) load_func( 'BN_CTX_init', [ BN_CTX ], None ) load_func( 'BN_CTX_free', [ BN_CTX ], None ) load_func( 'BN_cmp', [ BIGNUM, BIGNUM ], ctypes.c_int ) load_func( 'BN_num_bits', [ BIGNUM ], ctypes.c_int ) load_func( 'BN_add', [ BIGNUM, BIGNUM, BIGNUM ] ) load_func( 'BN_sub', [ BIGNUM, BIGNUM, BIGNUM ] ) load_func( 'BN_mul', [ BIGNUM, BIGNUM, BIGNUM, BN_CTX ] ) load_func( 'BN_div', [ BIGNUM, BIGNUM, BIGNUM, BIGNUM, BN_CTX ] ) load_func( 'BN_mod_exp', [ BIGNUM, BIGNUM, BIGNUM, BIGNUM, BN_CTX ] ) load_func( 'BN_rand', [ BIGNUM, ctypes.c_int, ctypes.c_int, ctypes.c_int ] ) load_func( 'BN_bn2bin', [ BIGNUM, ctypes.c_char_p ] ) load_func( 'BN_bin2bn', [ ctypes.c_char_p, ctypes.c_int, BIGNUM ], BIGNUM ) load_func( 'BN_hex2bn', [ ctypes.POINTER(BIGNUM), ctypes.c_char_p ] ) load_func( 'BN_bn2hex', [ BIGNUM ], ctypes.c_char_p ) load_func( 'CRYPTO_free', [ ctypes.c_char_p ] ) load_func( 'RAND_seed', [ ctypes.c_char_p, ctypes.c_int ] ) def BN_num_bytes(a): return ((BN_num_bits(a)+7)/8) def BN_mod(rem,m,d,ctx): return BN_div(None, rem, m, d, ctx) def BN_is_zero( n ): return n[0].top == 0 def bn_to_bytes( n ): b = ctypes.create_string_buffer( BN_num_bytes(n) ) BN_bn2bin(n, b) return b.raw def bytes_to_bn( dest_bn, bytes ): BN_bin2bn(bytes, len(bytes), dest_bn) def H_str( hash_class, dest_bn, s ): d = hash_class(s).digest() buff = ctypes.create_string_buffer( s ) BN_bin2bn(d, len(d), dest) def H_bn( hash_class, dest, n ): bin = ctypes.create_string_buffer( BN_num_bytes(n) ) BN_bn2bin(n, bin) d = hash_class( bin.raw ).digest() BN_bin2bn(d, len(d), dest) def H_bn_bn( hash_class, dest, n1, n2 ): h = hash_class() bin1 = ctypes.create_string_buffer( BN_num_bytes(n1) ) bin2 = ctypes.create_string_buffer( BN_num_bytes(n2) ) BN_bn2bin(n1, bin1) BN_bn2bin(n2, bin2) h.update( bin1.raw ) h.update( bin2.raw ) d = h.digest() BN_bin2bn(d, len(d), dest) def H_bn_str( hash_class, dest, n, s ): h = hash_class() bin = ctypes.create_string_buffer( BN_num_bytes(n) ) BN_bn2bin(n, bin) h.update( bin.raw ) h.update( s ) d = h.digest() BN_bin2bn(d, len(d), dest) def calculate_x( hash_class, dest, salt, username, password ): up = hash_class('%s:%s' % (username, password )).digest() H_bn_str( hash_class, dest, salt, up ) def update_hash( ctx, n ): buff = ctypes.create_string_buffer( BN_num_bytes(n) ) BN_bn2bin(n, buff) ctx.update( buff.raw ) def calculate_M( hash_class, N, g, I, s, A, B, K ): h = hash_class() h.update( HNxorg( hash_class, N, g ) ) h.update( hash_class(I).digest() ) update_hash( h, s ) update_hash( h, A ) update_hash( h, B ) h.update( K ) return h.digest() def calculate_H_AMK( hash_class, A, M, K ): h = hash_class() update_hash( h, A ) h.update( M ) h.update( K ) return h.digest() def HNxorg( hash_class, N, g ): bN = ctypes.create_string_buffer( BN_num_bytes(N) ) bg = ctypes.create_string_buffer( BN_num_bytes(g) ) BN_bn2bin(N, bN) BN_bn2bin(g, bg) hN = hash_class( bN.raw ).digest() hg = hash_class( bg.raw ).digest() return ''.join( chr( ord(hN[i]) ^ ord(hg[i]) ) for i in range(0,len(hN)) ) def get_ngk( hash_class, ng_type, n_hex, g_hex ): if ng_type < NG_CUSTOM: n_hex, g_hex = _ng_const[ ng_type ] N = BN_new() g = BN_new() k = BN_new() BN_hex2bn( N, n_hex ) BN_hex2bn( g, g_hex ) H_bn_bn(hash_class, k, N, g) return N, g, k def create_salted_verification_key( username, password, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None ): if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") s = BN_new() v = BN_new() x = BN_new() ctx = BN_CTX_new() hash_class = _hash_map[ hash_alg ] N,g,k = get_ngk( hash_class, ng_type, n_hex, g_hex ) BN_rand(s, 32, -1, 0); calculate_x( hash_class, x, s, username, password ) BN_mod_exp(v, g, x, N, ctx) salt = bn_to_bytes( s ) verifier = bn_to_bytes( v ) BN_free(s) BN_free(v) BN_free(x) BN_free(N) BN_free(g) BN_free(k) BN_CTX_free(ctx) return salt, verifier class Verifier (object): def __init__(self, username, bytes_s, bytes_v, bytes_A, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None): if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") self.A = BN_new() self.B = BN_new() self.K = None self.S = BN_new() self.u = BN_new() self.b = BN_new() self.s = BN_new() self.v = BN_new() self.tmp1 = BN_new() self.tmp2 = BN_new() self.ctx = BN_CTX_new() self.I = username self.M = None self.H_AMK = None self._authenticated = False self.safety_failed = False hash_class = _hash_map[ hash_alg ] N,g,k = get_ngk( hash_class, ng_type, n_hex, g_hex ) self.hash_class = hash_class self.N = N self.g = g self.k = k bytes_to_bn( self.s, bytes_s ) bytes_to_bn( self.v, bytes_v ) bytes_to_bn( self.A, bytes_A ) # SRP-6a safety check BN_mod(self.tmp1, self.A, N, self.ctx) if BN_is_zero(self.tmp1): self.safety_failed = True else: BN_rand(self.b, 256, -1, 0) # B = kv + g^b BN_mul(self.tmp1, k, self.v, self.ctx) BN_mod_exp(self.tmp2, g, self.b, N, self.ctx) BN_add(self.B, self.tmp1, self.tmp2) H_bn_bn(hash_class, self.u, self.A, self.B) # S = (A *(v^u)) ^ b BN_mod_exp(self.tmp1, self.v, self.u, N, self.ctx) BN_mul(self.tmp2, self.A, self.tmp1, self.ctx) BN_mod_exp(self.S, self.tmp2, self.b, N, self.ctx) self.K = hash_class( bn_to_bytes(self.S) ).digest() self.M = calculate_M( hash_class, N, g, self.I, self.s, self.A, self.B, self.K ) self.H_AMK = calculate_H_AMK( hash_class, self.A, self.M, self.K ) def __del__(self): if not hasattr(self, 'A'): return # __init__ threw exception. no clean up required BN_free(self.A) BN_free(self.B) BN_free(self.S) BN_free(self.u) BN_free(self.b) BN_free(self.s) BN_free(self.v) BN_free(self.N) BN_free(self.g) BN_free(self.k) BN_free(self.tmp1) BN_free(self.tmp2) BN_CTX_free(self.ctx) 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 (bn_to_bytes(self.s), bn_to_bytes(self.B)) def verify_session(self, user_M): if user_M == self.M: self._authenticated = True return self.H_AMK class User (object): def __init__(self, username, password, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None): if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") self.username = username self.password = password self.a = BN_new() self.A = BN_new() self.B = BN_new() self.s = BN_new() self.S = BN_new() self.u = BN_new() self.x = BN_new() self.v = BN_new() self.tmp1 = BN_new() self.tmp2 = BN_new() self.tmp3 = BN_new() self.ctx = BN_CTX_new() self.M = None self.K = None self.H_AMK = None self._authenticated = False hash_class = _hash_map[ hash_alg ] N,g,k = get_ngk( hash_class, ng_type, n_hex, g_hex ) self.hash_class = hash_class self.N = N self.g = g self.k = k BN_rand(self.a, 256, -1, 0) BN_mod_exp(self.A, g, self.a, N, self.ctx) def __del__(self): if not hasattr(self, 'a'): return # __init__ threw exception. no clean up required BN_free(self.a) BN_free(self.A) BN_free(self.B) BN_free(self.s) BN_free(self.S) BN_free(self.u) BN_free(self.x) BN_free(self.v) BN_free(self.N) BN_free(self.g) BN_free(self.k) BN_free(self.tmp1) BN_free(self.tmp2) BN_free(self.tmp3) BN_CTX_free(self.ctx) 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.username, bn_to_bytes(self.A)) # Returns M or None if SRP-6a safety check is violated def process_challenge(self, bytes_s, bytes_B): hash_class = self.hash_class N = self.N g = self.g k = self.k bytes_to_bn( self.s, bytes_s ) bytes_to_bn( self.B, bytes_B ) # SRP-6a safety check if BN_is_zero(self.B): return None H_bn_bn(hash_class, self.u, self.A, self.B) # SRP-6a safety check if BN_is_zero(self.u): return None calculate_x( hash_class, self.x, self.s, self.username, self.password ) BN_mod_exp(self.v, g, self.x, N, self.ctx) # S = (B - k*(g^x)) ^ (a + ux) BN_mul(self.tmp1, self.u, self.x, self.ctx) BN_add(self.tmp2, self.a, self.tmp1) # tmp2 = (a + ux) BN_mod_exp(self.tmp1, g, self.x, N, self.ctx) BN_mul(self.tmp3, k, self.tmp1, self.ctx) # tmp3 = k*(g^x) BN_sub(self.tmp1, self.B, self.tmp3) # tmp1 = (B - K*(g^x)) BN_mod_exp(self.S, self.tmp1, self.tmp2, N, self.ctx) self.K = hash_class( bn_to_bytes(self.S) ).digest() self.M = calculate_M( hash_class, N, g, self.username, self.s, self.A, self.B, self.K ) self.H_AMK = calculate_H_AMK( hash_class, self.A, self.M, self.K ) return self.M def verify_session(self, host_HAMK): if self.H_AMK == host_HAMK: self._authenticated = True #--------------------------------------------------------- # Init # RAND_seed( os.urandom(32), 32 ) pysrp-1.0.4/srp/_pysrp.py000066400000000000000000000303341221043136700153650ustar00rootroot00000000000000 # 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 SHA1 = 0 SHA224 = 1 SHA256 = 2 SHA384 = 3 SHA512 = 4 NG_1024 = 0 NG_2048 = 1 NG_4096 = 2 NG_8192 = 3 NG_CUSTOM = 4 _hash_map = { SHA1 : hashlib.sha1, SHA224 : hashlib.sha224, SHA256 : hashlib.sha256, SHA384 : hashlib.sha384, SHA512 : hashlib.sha512 } _ng_const = ( # 1024-bit ('''\ EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496\ EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E\ F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA\ 9AFD5138FE8376435B9FC61D2FC0EB06E3''', "2"), # 2048 ('''\ AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4\ A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60\ 95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF\ 747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907\ 8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861\ 60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB\ FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73''', "2"), # 4096 ('''\ FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ 8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ 302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ 49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ 180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ 3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ 04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ 1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ 99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ 04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ 233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199\ FFFFFFFFFFFFFFFF''', "5"), # 8192 ('''\ FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ 8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ 302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ 49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ 180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ 3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ 04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ 1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ 99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ 04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ 233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492\ 36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406\ AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918\ DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151\ 2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03\ F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F\ BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA\ CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B\ B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632\ 387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E\ 6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA\ 3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C\ 5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9\ 22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886\ 2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6\ 6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5\ 0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268\ 359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6\ FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71\ 60C980DD98EDD3DFFFFFFFFFFFFFFFFF''', '0x13') ) def get_ng( ng_type, n_hex, g_hex ): if ng_type < NG_CUSTOM: n_hex, g_hex = _ng_const[ ng_type ] return int(n_hex,16), int(g_hex,16) 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( hash_class, 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(hash_class(s).hexdigest(), 16) def H( hash_class, *args, **kwargs ): h = hash_class() 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) def HNxorg( hash_class, N, g ): hN = hash_class( long_to_bytes(N) ).digest() hg = hash_class( long_to_bytes(g) ).digest() return ''.join( chr( ord(hN[i]) ^ ord(hg[i]) ) for i in range(0,len(hN)) ) def gen_x( hash_class, salt, username, password ): return H( hash_class, salt, H( hash_class, username + ':' + password ) ) def create_salted_verification_key( username, password, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None ): if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") hash_class = _hash_map[ hash_alg ] N,g = get_ng( ng_type, n_hex, g_hex ) _s = long_to_bytes( get_random( 4 ) ) _v = long_to_bytes( pow(g, gen_x( hash_class, _s, username, password ), N) ) return _s, _v def calculate_M( hash_class, N, g, I, s, A, B, K ): h = hash_class() h.update( HNxorg( hash_class, N, g ) ) h.update( hash_class(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( hash_class, A, M, K ): h = hash_class() 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, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None): if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") self.s = bytes_to_long(bytes_s) self.v = bytes_to_long(bytes_v) self.I = username self.K = None self._authenticated = False N,g = get_ng( ng_type, n_hex, g_hex ) hash_class = _hash_map[ hash_alg ] k = H( hash_class, N, g ) self.hash_class = hash_class self.N = N self.g = g self.k = k 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(hash_class, self.A, self.B) self.S = pow(self.A*pow(self.v, self.u, N ), self.b, N) self.K = hash_class( long_to_bytes(self.S) ).digest() self.M = calculate_M( hash_class, N, g, self.I, self.s, self.A, self.B, self.K ) self.H_AMK = calculate_H_AMK( hash_class, 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, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None): if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") N,g = get_ng( ng_type, n_hex, g_hex ) hash_class = _hash_map[ hash_alg ] k = H( hash_class, N, g ) 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 self.hash_class = hash_class self.N = N self.g = g self.k = 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 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 ) N = self.N g = self.g k = self.k hash_class = self.hash_class # SRP-6a safety check if (self.B % N) == 0: return None self.u = H( hash_class, self.A, self.B ) # SRP-6a safety check if self.u == 0: return None self.x = gen_x( hash_class, 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 = hash_class( long_to_bytes(self.S) ).digest() self.M = calculate_M( hash_class, N, g, self.I, self.s, self.A, self.B, self.K ) self.H_AMK = calculate_H_AMK(hash_class, self.A, self.M, self.K) return self.M def verify_session(self, host_HAMK): if self.H_AMK == host_HAMK: self._authenticated = True pysrp-1.0.4/srp/_srp.c000066400000000000000000001433561221043136700146170ustar00rootroot00000000000000#include #include #include #include #include #include #include #include /*****************************************************************************/ /* Begin SRP Header */ /*****************************************************************************/ struct SRPVerifier; struct SRPUser; typedef enum { SRP_NG_1024, SRP_NG_2048, SRP_NG_4096, SRP_NG_8192, SRP_NG_CUSTOM } SRP_NGType; typedef enum { SRP_SHA1, SRP_SHA224, SRP_SHA256, SRP_SHA384, SRP_SHA512 } SRP_HashAlgorithm; /* This library will automatically seed the OpenSSL random number generator * using cryptographically sound random data on Windows & Linux. If this is * undesirable behavior or the host OS does not provide a /dev/urandom file, * this function may be called to seed the random number generator with * alternate data. * * Passing a null pointer to this function will cause this library to skip * seeding the random number generator. * * Notes: * * This function is optional on Windows & Linux. * * * This function is mandatory on all other platforms. Although it * will appear to work on other platforms, this library uses the current * time of day to seed the random number generator. This is well known to * be insecure. * * * When using this function, ensure the provided random data is * cryptographically strong. */ void srp_random_seed( const unsigned char * random_data, int data_length ); /* Out: bytes_s, len_s, bytes_v, len_v * * The caller is responsible for freeing the memory allocated for bytes_s and bytes_v * * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type. * If provided, they must contain ASCII text of the hexidecimal notation. */ void srp_create_salted_verification_key( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, const unsigned char * password, int len_password, const unsigned char ** bytes_s, int * len_s, const unsigned char ** bytes_v, int * len_v, const char * n_hex, const char * g_hex ); /* Out: bytes_B, len_B. * * On failure, bytes_B will be set to NULL and len_B will be set to 0 * * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type */ struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, const unsigned char * bytes_s, int len_s, const unsigned char * bytes_v, int len_v, const unsigned char * bytes_A, int len_A, const unsigned char ** bytes_B, int * len_B, const char * n_hex, const char * g_hex ); void srp_verifier_delete( struct SRPVerifier * ver ); int srp_verifier_is_authenticated( struct SRPVerifier * ver ); const char * srp_verifier_get_username( struct SRPVerifier * ver ); /* key_length may be null */ const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length ); int srp_verifier_get_session_key_length( struct SRPVerifier * ver ); /* user_M must be exactly srp_verifier_get_session_key_length() bytes in size */ void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char * user_M, const unsigned char ** bytes_HAMK ); /*******************************************************************************/ /* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type */ struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, const unsigned char * bytes_password, int len_password, const char * n_hex, const char * g_hex ); void srp_user_delete( struct SRPUser * usr ); int srp_user_is_authenticated( struct SRPUser * usr); const char * srp_user_get_username( struct SRPUser * usr ); /* key_length may be null */ const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_length ); int srp_user_get_session_key_length( struct SRPUser * usr ); /* Output: username, bytes_A, len_A */ void srp_user_start_authentication( struct SRPUser * usr, const char ** username, const unsigned char ** bytes_A, int * len_A ); /* Output: bytes_M, len_M (len_M may be null and will always be * srp_user_get_session_key_length() bytes in size) */ void srp_user_process_challenge( struct SRPUser * usr, const unsigned char * bytes_s, int len_s, const unsigned char * bytes_B, int len_B, const unsigned char ** bytes_M, int * len_M ); /* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */ void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK ); /*****************************************************************************/ /* Begin SRP Library */ /*****************************************************************************/ static int g_initialized = 0; typedef struct { BIGNUM * N; BIGNUM * g; } NGConstant; struct NGHex { const char * n_hex; const char * g_hex; }; /* All constants here were pulled from Appendix A of RFC 5054 */ static struct NGHex global_Ng_constants[] = { { /* 1024 */ "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496" "EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E" "F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA" "9AFD5138FE8376435B9FC61D2FC0EB06E3", "2" }, { /* 2048 */ "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4" "A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60" "95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF" "747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907" "8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861" "60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB" "FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73", "2" }, { /* 4096 */ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" "FFFFFFFFFFFFFFFF", "5" }, { /* 8192 */ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA" "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C" "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886" "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6" "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5" "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268" "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6" "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" "60C980DD98EDD3DFFFFFFFFFFFFFFFFF", "13" }, {0,0} /* null sentinel */ }; static NGConstant * new_ng( SRP_NGType ng_type, const char * n_hex, const char * g_hex ) { NGConstant * ng = (NGConstant *) malloc( sizeof(NGConstant) ); ng->N = BN_new(); ng->g = BN_new(); if ( ng_type != SRP_NG_CUSTOM ) { n_hex = global_Ng_constants[ ng_type ].n_hex; g_hex = global_Ng_constants[ ng_type ].g_hex; } BN_hex2bn( &ng->N, n_hex ); BN_hex2bn( &ng->g, g_hex ); return ng; } static void delete_ng( NGConstant * ng ) { BN_free( ng->N ); BN_free( ng->g ); ng->N = 0; ng->g = 0; free(ng); } typedef union { SHA_CTX sha; SHA256_CTX sha256; SHA512_CTX sha512; } HashCTX; struct SRPVerifier { SRP_HashAlgorithm hash_alg; NGConstant *ng; const char * username; const unsigned char * bytes_B; int authenticated; unsigned char M [SHA512_DIGEST_LENGTH]; unsigned char H_AMK [SHA512_DIGEST_LENGTH]; unsigned char session_key [SHA512_DIGEST_LENGTH]; }; struct SRPUser { SRP_HashAlgorithm hash_alg; NGConstant *ng; BIGNUM *a; BIGNUM *A; BIGNUM *S; const unsigned char * bytes_A; int authenticated; const char * username; const unsigned char * password; int password_len; unsigned char M [SHA512_DIGEST_LENGTH]; unsigned char H_AMK [SHA512_DIGEST_LENGTH]; unsigned char session_key [SHA512_DIGEST_LENGTH]; }; static int hash_init( SRP_HashAlgorithm alg, HashCTX *c ) { switch (alg) { case SRP_SHA1 : return SHA1_Init( &c->sha ); case SRP_SHA224: return SHA224_Init( &c->sha256 ); case SRP_SHA256: return SHA256_Init( &c->sha256 ); case SRP_SHA384: return SHA384_Init( &c->sha512 ); case SRP_SHA512: return SHA512_Init( &c->sha512 ); default: return -1; }; } static int hash_update( SRP_HashAlgorithm alg, HashCTX *c, const void *data, size_t len ) { switch (alg) { case SRP_SHA1 : return SHA1_Update( &c->sha, data, len ); case SRP_SHA224: return SHA224_Update( &c->sha256, data, len ); case SRP_SHA256: return SHA256_Update( &c->sha256, data, len ); case SRP_SHA384: return SHA384_Update( &c->sha512, data, len ); case SRP_SHA512: return SHA512_Update( &c->sha512, data, len ); default: return -1; }; } static int hash_final( SRP_HashAlgorithm alg, HashCTX *c, unsigned char *md ) { switch (alg) { case SRP_SHA1 : return SHA1_Final( md, &c->sha ); case SRP_SHA224: return SHA224_Final( md, &c->sha256 ); case SRP_SHA256: return SHA256_Final( md, &c->sha256 ); case SRP_SHA384: return SHA384_Final( md, &c->sha512 ); case SRP_SHA512: return SHA512_Final( md, &c->sha512 ); default: return -1; }; } static unsigned char * hash( SRP_HashAlgorithm alg, const unsigned char *d, size_t n, unsigned char *md ) { switch (alg) { case SRP_SHA1 : return SHA1( d, n, md ); case SRP_SHA224: return SHA224( d, n, md ); case SRP_SHA256: return SHA256( d, n, md ); case SRP_SHA384: return SHA384( d, n, md ); case SRP_SHA512: return SHA512( d, n, md ); default: return 0; }; } static int hash_length( SRP_HashAlgorithm alg ) { switch (alg) { case SRP_SHA1 : return SHA_DIGEST_LENGTH; case SRP_SHA224: return SHA224_DIGEST_LENGTH; case SRP_SHA256: return SHA256_DIGEST_LENGTH; case SRP_SHA384: return SHA384_DIGEST_LENGTH; case SRP_SHA512: return SHA512_DIGEST_LENGTH; default: return -1; }; } static BIGNUM * H_nn( SRP_HashAlgorithm alg, const BIGNUM * n1, const BIGNUM * n2 ) { unsigned char buff[ SHA512_DIGEST_LENGTH ]; int len_n1 = BN_num_bytes(n1); int len_n2 = BN_num_bytes(n2); int nbytes = len_n1 + len_n2; unsigned char * bin = (unsigned char *) malloc( nbytes ); BN_bn2bin(n1, bin); BN_bn2bin(n2, bin + len_n1); hash( alg, bin, nbytes, buff ); free(bin); return BN_bin2bn(buff, hash_length(alg), NULL); } static BIGNUM * H_ns( SRP_HashAlgorithm alg, const BIGNUM * n, const unsigned char * bytes, int len_bytes ) { unsigned char buff[ SHA512_DIGEST_LENGTH ]; int len_n = BN_num_bytes(n); int nbytes = len_n + len_bytes; unsigned char * bin = (unsigned char *) malloc( nbytes ); BN_bn2bin(n, bin); memcpy( bin + len_n, bytes, len_bytes ); hash( alg, bin, nbytes, buff ); free(bin); return BN_bin2bn(buff, hash_length(alg), NULL); } static BIGNUM * calculate_x( SRP_HashAlgorithm alg, const BIGNUM * salt, const char * username, const unsigned char * password, int password_len ) { unsigned char ucp_hash[SHA512_DIGEST_LENGTH]; HashCTX ctx; hash_init( alg, &ctx ); hash_update( alg, &ctx, username, strlen(username) ); hash_update( alg, &ctx, ":", 1 ); hash_update( alg, &ctx, password, password_len ); hash_final( alg, &ctx, ucp_hash ); return H_ns( alg, salt, ucp_hash, hash_length(alg) ); } static void update_hash_n( SRP_HashAlgorithm alg, HashCTX *ctx, const BIGNUM * n ) { unsigned long len = BN_num_bytes(n); unsigned char * n_bytes = (unsigned char *) malloc( len ); BN_bn2bin(n, n_bytes); hash_update(alg, ctx, n_bytes, len); free(n_bytes); } static void hash_num( SRP_HashAlgorithm alg, const BIGNUM * n, unsigned char * dest ) { int nbytes = BN_num_bytes(n); unsigned char * bin = (unsigned char *) malloc( nbytes ); BN_bn2bin(n, bin); hash( alg, bin, nbytes, dest ); free(bin); } static void calculate_M( SRP_HashAlgorithm alg, NGConstant *ng, unsigned char * dest, const char * I, const BIGNUM * s, const BIGNUM * A, const BIGNUM * B, const unsigned char * K ) { unsigned char H_N[ SHA512_DIGEST_LENGTH ]; unsigned char H_g[ SHA512_DIGEST_LENGTH ]; unsigned char H_I[ SHA512_DIGEST_LENGTH ]; unsigned char H_xor[ SHA512_DIGEST_LENGTH ]; HashCTX ctx; int i = 0; int hash_len = hash_length(alg); hash_num( alg, ng->N, H_N ); hash_num( alg, ng->g, H_g ); hash(alg, (const unsigned char *)I, strlen(I), H_I); for (i=0; i < hash_len; i++ ) H_xor[i] = H_N[i] ^ H_g[i]; hash_init( alg, &ctx ); hash_update( alg, &ctx, H_xor, hash_len ); hash_update( alg, &ctx, H_I, hash_len ); update_hash_n( alg, &ctx, s ); update_hash_n( alg, &ctx, A ); update_hash_n( alg, &ctx, B ); hash_update( alg, &ctx, K, hash_len ); hash_final( alg, &ctx, dest ); } static void calculate_H_AMK( SRP_HashAlgorithm alg, unsigned char *dest, const BIGNUM * A, const unsigned char * M, const unsigned char * K ) { HashCTX ctx; hash_init( alg, &ctx ); update_hash_n( alg, &ctx, A ); hash_update( alg, &ctx, M, hash_length(alg) ); hash_update( alg, &ctx, K, hash_length(alg) ); hash_final( alg, &ctx, dest ); } /* Python module calls random_seed during module initialization */ #define init_random() /*********************************************************************************************************** * * Exported Functions * ***********************************************************************************************************/ void srp_random_seed( const unsigned char * random_data, int data_length ) { g_initialized = 1; if (random_data) RAND_seed( random_data, data_length ); } void srp_create_salted_verification_key( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, const unsigned char * password, int len_password, const unsigned char ** bytes_s, int * len_s, const unsigned char ** bytes_v, int * len_v, const char * n_hex, const char * g_hex ) { BIGNUM * s = BN_new(); BIGNUM * v = BN_new(); BIGNUM * x = 0; BN_CTX * ctx = BN_CTX_new(); NGConstant * ng = new_ng( ng_type, n_hex, g_hex ); init_random(); /* Only happens once */ BN_rand(s, 32, -1, 0); x = calculate_x( alg, s, username, password, len_password ); BN_mod_exp(v, ng->g, x, ng->N, ctx); *len_s = BN_num_bytes(s); *len_v = BN_num_bytes(v); *bytes_s = (const unsigned char *) malloc( *len_s ); *bytes_v = (const unsigned char *) malloc( *len_v ); BN_bn2bin(s, (unsigned char *) *bytes_s); BN_bn2bin(v, (unsigned char *) *bytes_v); delete_ng( ng ); BN_free(s); BN_free(v); BN_free(x); BN_CTX_free(ctx); } /* Out: bytes_B, len_B. * * On failure, bytes_B will be set to NULL and len_B will be set to 0 */ struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, const unsigned char * bytes_s, int len_s, const unsigned char * bytes_v, int len_v, const unsigned char * bytes_A, int len_A, const unsigned char ** bytes_B, int * len_B, const char * n_hex, const char * g_hex ) { BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL); BIGNUM *v = BN_bin2bn(bytes_v, len_v, NULL); BIGNUM *A = BN_bin2bn(bytes_A, len_A, NULL); BIGNUM *u = 0; BIGNUM *B = BN_new(); BIGNUM *S = BN_new(); BIGNUM *b = BN_new(); BIGNUM *k = 0; BIGNUM *tmp1 = BN_new(); BIGNUM *tmp2 = BN_new(); BN_CTX *ctx = BN_CTX_new(); int ulen = strlen(username) + 1; NGConstant *ng = new_ng( ng_type, n_hex, g_hex ); struct SRPVerifier * ver = (struct SRPVerifier *) malloc( sizeof(struct SRPVerifier) ); init_random(); /* Only happens once */ ver->username = (char *) malloc( ulen ); ver->hash_alg = alg; ver->ng = ng; memcpy( (char*)ver->username, username, ulen ); ver->authenticated = 0; /* SRP-6a safety check */ BN_mod(tmp1, A, ng->N, ctx); if ( !BN_is_zero(tmp1) ) { BN_rand(b, 256, -1, 0); k = H_nn(alg, ng->N, ng->g); /* B = kv + g^b */ BN_mul(tmp1, k, v, ctx); BN_mod_exp(tmp2, ng->g, b, ng->N, ctx); BN_add(B, tmp1, tmp2); u = H_nn(alg, A, B); /* S = (A *(v^u)) ^ b */ BN_mod_exp(tmp1, v, u, ng->N, ctx); BN_mul(tmp2, A, tmp1, ctx); BN_mod_exp(S, tmp2, b, ng->N, ctx); hash_num(alg, S, ver->session_key); calculate_M( alg, ng, ver->M, username, s, A, B, ver->session_key ); calculate_H_AMK( alg, ver->H_AMK, A, ver->M, ver->session_key ); *len_B = BN_num_bytes(B); *bytes_B = malloc( *len_B ); BN_bn2bin( B, (unsigned char *) *bytes_B ); ver->bytes_B = *bytes_B; } else { *len_B = 0; *bytes_B = NULL; } BN_free(s); BN_free(v); BN_free(A); if (u) BN_free(u); if (k) BN_free(k); BN_free(B); BN_free(S); BN_free(b); BN_free(tmp1); BN_free(tmp2); BN_CTX_free(ctx); return ver; } void srp_verifier_delete( struct SRPVerifier * ver ) { delete_ng( ver->ng ); free( (char *) ver->username ); free( (unsigned char *) ver->bytes_B ); free( ver ); } int srp_verifier_is_authenticated( struct SRPVerifier * ver ) { return ver->authenticated; } const char * srp_verifier_get_username( struct SRPVerifier * ver ) { return ver->username; } const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length ) { if (key_length) *key_length = hash_length( ver->hash_alg ); return ver->session_key; } int srp_verifier_get_session_key_length( struct SRPVerifier * ver ) { return hash_length( ver->hash_alg ); } /* user_M must be exactly SHA512_DIGEST_LENGTH bytes in size */ void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char * user_M, const unsigned char ** bytes_HAMK ) { if ( memcmp( ver->M, user_M, hash_length(ver->hash_alg) ) == 0 ) { ver->authenticated = 1; *bytes_HAMK = ver->H_AMK; } else *bytes_HAMK = NULL; } /*******************************************************************************/ struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, const unsigned char * bytes_password, int len_password, const char * n_hex, const char * g_hex ) { struct SRPUser *usr = (struct SRPUser *) malloc( sizeof(struct SRPUser) ); int ulen = strlen(username) + 1; init_random(); /* Only happens once */ usr->hash_alg = alg; usr->ng = new_ng( ng_type, n_hex, g_hex ); usr->a = BN_new(); usr->A = BN_new(); usr->S = BN_new(); usr->username = (const char *) malloc(ulen); usr->password = (const unsigned char *) malloc(len_password); usr->password_len = len_password; memcpy((char *)usr->username, username, ulen); memcpy((char *)usr->password, bytes_password, len_password); usr->authenticated = 0; usr->bytes_A = 0; return usr; } void srp_user_delete( struct SRPUser * usr ) { BN_free( usr->a ); BN_free( usr->A ); BN_free( usr->S ); delete_ng( usr->ng ); free((char *)usr->username); free((char *)usr->password); if (usr->bytes_A) free( (char *)usr->bytes_A ); free( usr ); } int srp_user_is_authenticated( struct SRPUser * usr) { return usr->authenticated; } const char * srp_user_get_username( struct SRPUser * usr ) { return usr->username; } const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_length ) { if (key_length) *key_length = hash_length( usr->hash_alg ); return usr->session_key; } int srp_user_get_session_key_length( struct SRPUser * usr ) { return hash_length( usr->hash_alg ); } /* Output: username, bytes_A, len_A */ void srp_user_start_authentication( struct SRPUser * usr, const char ** username, const unsigned char ** bytes_A, int * len_A ) { BN_CTX *ctx = BN_CTX_new(); BN_rand(usr->a, 256, -1, 0); BN_mod_exp(usr->A, usr->ng->g, usr->a, usr->ng->N, ctx); BN_CTX_free(ctx); *len_A = BN_num_bytes(usr->A); *bytes_A = malloc( *len_A ); BN_bn2bin( usr->A, (unsigned char *) *bytes_A ); usr->bytes_A = *bytes_A; *username = usr->username; } /* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */ void srp_user_process_challenge( struct SRPUser * usr, const unsigned char * bytes_s, int len_s, const unsigned char * bytes_B, int len_B, const unsigned char ** bytes_M, int * len_M ) { BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL); BIGNUM *B = BN_bin2bn(bytes_B, len_B, NULL); BIGNUM *u = 0; BIGNUM *x = 0; BIGNUM *k = 0; BIGNUM *v = BN_new(); BIGNUM *tmp1 = BN_new(); BIGNUM *tmp2 = BN_new(); BIGNUM *tmp3 = BN_new(); BN_CTX *ctx = BN_CTX_new(); u = H_nn(usr->hash_alg, usr->A, B); x = calculate_x( usr->hash_alg, s, usr->username, usr->password, usr->password_len ); k = H_nn(usr->hash_alg, usr->ng->N, usr->ng->g); /* SRP-6a safety check */ if ( !BN_is_zero(B) && !BN_is_zero(u) ) { BN_mod_exp(v, usr->ng->g, x, usr->ng->N, ctx); /* S = (B - k*(g^x)) ^ (a + ux) */ BN_mul(tmp1, u, x, ctx); BN_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */ BN_mod_exp(tmp1, usr->ng->g, x, usr->ng->N, ctx); BN_mul(tmp3, k, tmp1, ctx); /* tmp3 = k*(g^x) */ BN_sub(tmp1, B, tmp3); /* tmp1 = (B - K*(g^x)) */ BN_mod_exp(usr->S, tmp1, tmp2, usr->ng->N, ctx); hash_num(usr->hash_alg, usr->S, usr->session_key); calculate_M( usr->hash_alg, usr->ng, usr->M, usr->username, s, usr->A, B, usr->session_key ); calculate_H_AMK( usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key ); *bytes_M = usr->M; if (len_M) *len_M = hash_length( usr->hash_alg ); } else { *bytes_M = NULL; if (len_M) *len_M = 0; } BN_free(s); BN_free(B); BN_free(u); BN_free(x); BN_free(k); BN_free(v); BN_free(tmp1); BN_free(tmp2); BN_free(tmp3); BN_CTX_free(ctx); } void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK ) { if ( memcmp( usr->H_AMK, bytes_HAMK, hash_length(usr->hash_alg) ) == 0 ) usr->authenticated = 1; } /****************************************************************************** * * Python Module * *****************************************************************************/ typedef struct { PyObject_HEAD struct SRPVerifier * ver; const unsigned char * bytes_B; const unsigned char * bytes_s; int len_B; int len_s; }PyVerifier; typedef struct { PyObject_HEAD struct SRPUser * usr; }PyUser; static void ver_dealloc( PyVerifier * self ) { if ( self->ver != NULL ) srp_verifier_delete( self->ver ); if ( self->bytes_s != NULL ) free( (char *)self->bytes_s ); self->ob_type->tp_free( (PyObject *) self ); } static void usr_dealloc( PyUser * self ) { if ( self->usr != NULL ) srp_user_delete( self->usr ); self->ob_type->tp_free( (PyObject *) self ); } static PyObject * ver_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyVerifier *self = (PyVerifier *) type->tp_alloc(type, 0); if (!self) return NULL; self->ver = NULL; self->bytes_B = NULL; self->bytes_s = NULL; self->len_B = 0; self->len_s = 0; return (PyObject *) self; } static PyObject * usr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyUser *self = (PyUser *) type->tp_alloc(type, 0); if (!self) return NULL; self->usr = NULL; return (PyObject *) self; } static int ver_init( PyVerifier *self, PyObject *args, PyObject *kwds ) { const char *username; const unsigned char *bytes_s, *bytes_v, *bytes_A; int len_s, len_v, len_A; int hash_alg = SRP_SHA1; int ng_type = SRP_NG_2048; const char *n_hex = 0; const char *g_hex = 0; static char * kwnames[] = { "username", "bytes_s", "bytes_v", "bytes_A", "hash_alg", "ng_type", "n_hex", "g_hex", NULL }; if ( self->ver != NULL ) { PyErr_SetString(PyExc_TypeError, "Type cannot be re-initialized"); return -1; } if ( ! PyArg_ParseTupleAndKeywords(args, kwds, "st#t#t#|iiss", kwnames, &username, &bytes_s, &len_s, &bytes_v, &len_v, &bytes_A, &len_A, &hash_alg, &ng_type, &n_hex, &g_hex ) ) { return -1; } if ( hash_alg < SRP_SHA1 || hash_alg > SRP_SHA512 ) { PyErr_SetString(PyExc_ValueError, "Invalid Hash Algorithm"); return -1; } if ( ng_type < SRP_NG_1024 || ng_type > SRP_NG_CUSTOM ) { PyErr_SetString(PyExc_ValueError, "Invalid Prime Number Constant"); return -1; } if ( ng_type == SRP_NG_CUSTOM && ( !n_hex || !g_hex ) ) { PyErr_SetString(PyExc_ValueError, "Both n_hex and g_hex are required when ng_type = NG_CUSTOM"); return -1; } /* The srp_verifier_new command is computationally intensive. Allowing multiple, * simultaneous calls here will speed things up for multi-cpu machines */ Py_BEGIN_ALLOW_THREADS self->ver = srp_verifier_new( (SRP_HashAlgorithm) hash_alg, (SRP_NGType) ng_type, username, bytes_s, len_s, bytes_v, len_v, bytes_A, len_A, &self->bytes_B, &self->len_B, n_hex, g_hex ); Py_END_ALLOW_THREADS if ( self->bytes_B == NULL ) { PyErr_SetString(PyExc_Exception, "SRP-6a safety check violated"); return -1; } self->bytes_s = malloc( len_s ); self->len_s = len_s; memcpy( (char *)self->bytes_s, bytes_s, len_s ); return 0; } static int usr_init( PyUser *self, PyObject *args, PyObject *kwds ) { const char *username = 0; const unsigned char *bytes_password = 0; int len_password = 0; int hash_alg = SRP_SHA1; int ng_type = SRP_NG_2048; const char *n_hex = 0; const char *g_hex = 0; static char * kwnames[] = { "username", "password", "hash_alg", "ng_type", "n_hex", "g_hex", NULL }; if ( self->usr != NULL ) { PyErr_SetString(PyExc_TypeError, "Type cannot be re-initialized"); return -1; } if ( ! PyArg_ParseTupleAndKeywords(args, kwds, "st#|iiss", kwnames, &username, &bytes_password, &len_password, &hash_alg, &ng_type, &n_hex, &g_hex) ) { return -1; } if ( hash_alg < SRP_SHA1 || hash_alg > SRP_SHA512 ) { PyErr_SetString(PyExc_ValueError, "Invalid Hash Algorithm"); return -1; } if ( ng_type < SRP_NG_1024 || ng_type > SRP_NG_CUSTOM ) { PyErr_SetString(PyExc_ValueError, "Invalid Prime Number Constant"); return -1; } if ( ng_type == SRP_NG_CUSTOM && ( !n_hex || !g_hex ) ) { PyErr_SetString(PyExc_ValueError, "Both n_hex and g_hex are required when ng_type = NG_CUSTOM"); return -1; } self->usr = srp_user_new( (SRP_HashAlgorithm) hash_alg, (SRP_NGType) ng_type, username, bytes_password, len_password, n_hex, g_hex ); return 0; } static PyObject * ver_is_authenticated( PyVerifier * self ) { if ( self->ver == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } if ( srp_verifier_is_authenticated(self->ver) ) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static PyObject * usr_is_authenticated( PyUser * self ) { if ( self->usr == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } if ( srp_user_is_authenticated(self->usr) ) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static PyObject * ver_get_username( PyVerifier * self ) { if ( self->ver == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } return PyString_FromString( srp_verifier_get_username(self->ver) ); } static PyObject * usr_get_username( PyUser * self ) { if ( self->usr == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } return PyString_FromString( srp_user_get_username(self->usr) ); } static PyObject * ver_get_session_key( PyVerifier * self ) { if ( self->ver == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } if ( srp_verifier_is_authenticated(self->ver) ) { int key_len; const char * u = (const char *)srp_verifier_get_session_key(self->ver, &key_len); return PyString_FromStringAndSize(u, key_len); } else Py_RETURN_NONE; } static PyObject * usr_get_session_key( PyUser * self ) { if ( self->usr == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } if ( srp_user_is_authenticated(self->usr) ) { int key_len; const char * u = (const char *) srp_user_get_session_key(self->usr, &key_len); return PyString_FromStringAndSize(u, key_len); } else Py_RETURN_NONE; } static PyObject * ver_get_challenge( PyVerifier * self ) { if ( self->ver == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } if ( self->bytes_B == NULL ) { PyErr_SetString(PyExc_Exception, "SRP-6a security check failed"); return NULL; } return Py_BuildValue("s#s#", self->bytes_s, self->len_s, self->bytes_B, self->len_B); } static PyObject * ver_verify_session( PyVerifier * self, PyObject * args ) { const unsigned char * bytes_M; const unsigned char * bytes_HAMK; int len_M; if ( self->ver == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } if ( ! PyArg_ParseTuple(args, "t#", &bytes_M, &len_M) ) { return NULL; } if ( len_M != srp_verifier_get_session_key_length( self->ver ) ) Py_RETURN_NONE; srp_verifier_verify_session( self->ver, bytes_M, &bytes_HAMK ); if ( bytes_HAMK == NULL ) Py_RETURN_NONE; else return PyString_FromStringAndSize((const char *) bytes_HAMK, srp_verifier_get_session_key_length( self->ver )); } static PyObject * usr_start_authentication( PyUser * self ) { const char * username; const unsigned char * bytes_A; int len_A; if ( self->usr == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } srp_user_start_authentication( self->usr, &username, &bytes_A, &len_A ); return Py_BuildValue("ss#", username, bytes_A, len_A); } static PyObject * usr_process_challenge( PyUser * self, PyObject * args ) { const unsigned char * bytes_s, *bytes_B; int len_s, len_B, len_M; const unsigned char * bytes_M; if ( self->usr == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } if ( ! PyArg_ParseTuple(args, "t#t#", &bytes_s, &len_s, &bytes_B, &len_B) ) { return NULL; } /* The srp_user_process_challenge command is computationally intensive. * Allowing multiple, simultaneous calls here will speed things up on * multi-cpu machines. */ Py_BEGIN_ALLOW_THREADS srp_user_process_challenge( self->usr, bytes_s, len_s, bytes_B, len_B, &bytes_M, &len_M ); Py_END_ALLOW_THREADS if (bytes_M == NULL) Py_RETURN_NONE; else return PyString_FromStringAndSize((const char *) bytes_M, len_M); } static PyObject * usr_verify_session( PyUser * self, PyObject * args ) { const unsigned char * bytes_HAMK; int len_HAMK; if ( self->usr == NULL ) { PyErr_SetString(PyExc_Exception, "Type not initialized"); return NULL; } if ( ! PyArg_ParseTuple(args, "t#", &bytes_HAMK, &len_HAMK) ) { return NULL; } if ( len_HAMK == srp_user_get_session_key_length( self->usr ) ) srp_user_verify_session( self->usr, bytes_HAMK ); Py_RETURN_NONE; } static PyObject * py_create_salted_verification_key( PyObject *self, PyObject *args, PyObject *kwds ) { PyObject *ret; const char *username; const unsigned char *bytes_password, *bytes_s, *bytes_v; int len_password, len_s, len_v; int hash_alg = SRP_SHA1; int ng_type = SRP_NG_2048; const char *n_hex = 0; const char *g_hex = 0; static char * kwnames[] = { "username", "password", "hash_alg", "ng_type", "n_hex", "g_hex", NULL }; if ( ! PyArg_ParseTupleAndKeywords(args, kwds, "st#|iiss", kwnames, &username, &bytes_password, &len_password, &hash_alg, &ng_type, &n_hex, &g_hex) ) return NULL; if ( hash_alg < SRP_SHA1 || hash_alg > SRP_SHA512 ) { PyErr_SetString(PyExc_ValueError, "Invalid Hash Algorithm"); return NULL; } if ( ng_type < SRP_NG_1024 || ng_type > SRP_NG_CUSTOM ) { PyErr_SetString(PyExc_ValueError, "Invalid Prime Number Constant"); return NULL; } if ( ng_type == SRP_NG_CUSTOM && ( !n_hex || !g_hex ) ) { PyErr_SetString(PyExc_ValueError, "Both n_hex and g_hex are required when ng_type = NG_CUSTOM"); return NULL; } srp_create_salted_verification_key( (SRP_HashAlgorithm) hash_alg, (SRP_NGType) ng_type, username, bytes_password, len_password, &bytes_s, &len_s, &bytes_v, &len_v, n_hex, g_hex ); ret = Py_BuildValue("s#s#", bytes_s, len_s, bytes_v, len_v); free((char*)bytes_s); free((char*)bytes_v); return ret; } /***********************************************************************************/ static PyMethodDef PyVerifier_methods[] = { {"authenticated", (PyCFunction) ver_is_authenticated, METH_NOARGS, PyDoc_STR("Returns boolean indicating whether the session is " "authenticated or not") }, {"get_username", (PyCFunction) ver_get_username, METH_NOARGS, PyDoc_STR("Returns the username the Verifier instance is bound to.") }, {"get_session_key", (PyCFunction) ver_get_session_key, METH_NOARGS, PyDoc_STR("Returns the session key for an authenticated session. " "Returns None if the session is not authenticated.") }, {"get_challenge", (PyCFunction) ver_get_challenge, METH_NOARGS, PyDoc_STR("Returns: (s,B) or None. The salt & challenge that " "should be sent to the user or None if the SRP-6a " "safety check fails.") }, {"verify_session", (PyCFunction) ver_verify_session, METH_VARARGS, PyDoc_STR("Verifies the user based on their reply to " "the challenge") }, {NULL} /* Sentinel */ }; static PyMethodDef PyUser_methods[] = { {"authenticated", (PyCFunction) usr_is_authenticated, METH_NOARGS, PyDoc_STR("Returns boolean indicating whether the session is " "authenticated or not") }, {"get_username", (PyCFunction) usr_get_username, METH_NOARGS, PyDoc_STR("Returns the username the User instance is bound to.") }, {"get_session_key", (PyCFunction) usr_get_session_key, METH_NOARGS, PyDoc_STR("Returns the session key for an authenticated session. " "Returns None if the session is not authenticated.") }, {"start_authentication", (PyCFunction) usr_start_authentication, METH_NOARGS, PyDoc_STR("Returns (username,A). The username and initial " "authentication challenge to send to the verifier") }, {"process_challenge", (PyCFunction) usr_process_challenge, METH_VARARGS, PyDoc_STR("Returns the reply to send to the server or None if the " "SRP-6a safety check fails") }, {"verify_session", (PyCFunction) usr_verify_session, METH_VARARGS, PyDoc_STR("Verifies the server based on its reply to the users " "challenge response") }, {NULL} /* Sentinel */ }; static PyMethodDef srp_module_methods[] = { {"create_salted_verification_key", (PyCFunction) py_create_salted_verification_key, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Returns (s,v): Generates a salt & verifier for the " "given username and password") }, {NULL} /* Sentinel */ }; static PyTypeObject PyVerifier_Type = { PyVarObject_HEAD_INIT(NULL, 0) "srp._srp.Verifier", /*tp_name*/ sizeof(PyVerifier), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)ver_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "SRP-6a verfier", /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ PyVerifier_methods, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ (initproc)ver_init, /*tp_init*/ 0, /*tp_alloc*/ ver_new, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; static PyTypeObject PyUser_Type = { PyVarObject_HEAD_INIT(NULL, 0) "srp._srp.User", /*tp_name*/ sizeof(PyUser), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)usr_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "SRP-6a User", /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ PyUser_methods, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ (initproc)usr_init, /*tp_init*/ 0, /*tp_alloc*/ usr_new, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; PyMODINIT_FUNC init_srp(void) { int init_ok = 0; PyObject *m = NULL; PyObject *os = NULL; PyObject *py_urandom = NULL; os = PyImport_ImportModule("os"); if (os == NULL) return; py_urandom = PyObject_GetAttrString(os, "urandom"); if ( py_urandom && PyCallable_Check(py_urandom) ) { PyObject *args = Py_BuildValue("(i)", 32); if ( args ) { PyObject *randstr = PyObject_CallObject(py_urandom, args); if ( randstr && PyString_Check(randstr)) { char *buff = NULL; Py_ssize_t slen = 0; if (!PyString_AsStringAndSize(randstr, &buff, &slen)) { srp_random_seed( (const unsigned char *)buff, slen ); init_ok = 1; } } Py_XDECREF(randstr); } Py_XDECREF(args); } Py_XDECREF(os); Py_XDECREF(py_urandom); if (!init_ok) { PyErr_SetString(PyExc_ImportError, "Initialization failed"); return; } if (PyType_Ready(&PyVerifier_Type) < 0 || PyType_Ready(&PyUser_Type) < 0) return; m = Py_InitModule3("srp._srp", srp_module_methods,"SRP-6a implementation"); if (m == NULL) return; Py_INCREF(&PyVerifier_Type); Py_INCREF(&PyUser_Type); PyModule_AddObject(m, "Verifier", (PyObject*) &PyVerifier_Type ); PyModule_AddObject(m, "User", (PyObject*) &PyUser_Type ); PyModule_AddIntConstant(m, "NG_1024", SRP_NG_1024); PyModule_AddIntConstant(m, "NG_2048", SRP_NG_2048); PyModule_AddIntConstant(m, "NG_4096", SRP_NG_4096); PyModule_AddIntConstant(m, "NG_8192", SRP_NG_8192); PyModule_AddIntConstant(m, "NG_CUSTOM", SRP_NG_CUSTOM); PyModule_AddIntConstant(m, "SHA1", SRP_SHA1); PyModule_AddIntConstant(m, "SHA224", SRP_SHA224); PyModule_AddIntConstant(m, "SHA256", SRP_SHA256); PyModule_AddIntConstant(m, "SHA384", SRP_SHA384); PyModule_AddIntConstant(m, "SHA512", SRP_SHA512); } pysrp-1.0.4/srp/doc/000077500000000000000000000000001221043136700142415ustar00rootroot00000000000000pysrp-1.0.4/srp/doc/conf.py000066400000000000000000000156351221043136700155520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Secure Remote Password documentation build configuration file, created by # sphinx-quickstart on Fri Mar 25 10:20:52 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Secure Remote Password' copyright = u'2011, Tom Cocagne' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.0' # The full version, including alpha/beta/rc tags. release = '1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'SecureRemotePassworddoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'SecureRemotePassword.tex', u'Secure Remote Password Documentation', u'Tom Cocagne', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'secureremotepassword', u'Secure Remote Password Documentation', [u'Tom Cocagne'], 1) ] pysrp-1.0.4/srp/doc/index.rst000066400000000000000000000007411221043136700161040ustar00rootroot00000000000000.. Secure Remote Password documentation master file, created by sphinx-quickstart on Fri Mar 25 10:20:52 2011. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to Secure Remote Password's documentation! ================================================== Contents: .. toctree:: :maxdepth: 2 srp.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` pysrp-1.0.4/srp/doc/srp.rst000066400000000000000000000314051221043136700156020ustar00rootroot00000000000000:mod:`srp` --- Secure Remote Password ===================================== .. module:: srp :synopsis: Secure Remote Password .. moduleauthor:: Tom Cocagne .. sectionauthor:: Tom Cocagne The Secure Remote Password protocol (SRP) is a cryptographically strong authentication protocol for password-based, mutual authentication over an insecure network connection. Successful SRP authentication requires both sides of the connection to have knowledge of the user's password. In addition to password verification, the SRP protocol also performs a secure key exchange during the authentication process. This key may be used to protect network traffic via symmetric key encryption. SRP offers security and deployment advantages over other challenge-response protocols, such as Kerberos and SSL, in that it does not require trusted key servers or certificate infrastructures. Instead, small verification keys derived from each user's password are stored and used by each SRP server application. SRP provides a near-ideal solution for many applications requiring simple and secure password authentication that does not rely on an external infrastructure. Another favorable aspect of the SRP protocol is that compromized verification keys are of little value to an attacker. Possesion of a verification key does not allow a user to be impersonated and it cannot be used to obtain the users password except by way of a computationally infeasible dictionary attack. A compromized key would, however, allow an attacker to impersonate the server side of an SRP authenticated connection. Consequently, care should be taken to prevent unauthorized access to verification keys for applications in which the client side relies on the server being genuine. Usage ----- SRP usage begins with *create_salted_verification_key()*. This function creates a salted verification key from the user's password. The resulting salt and key are stored by the server application and will be used during the authentication process. The authentication process occurs as an exchange of messages between the clent and the server. The :ref:`example` below provides a simple demonstration of the protocol. A comprehensive description of the SRP protocol is contained in the :ref:`protocol-description` section. The *User* & *Verifier* constructors, as well as the *create_salted_verification_key()* function, accept optional arguments to specify which hashing algorithm and prime number arguments should be used during the authentication process. These options may be used to tune the security/performance tradeoff for an application. Generally speaking, specifying arguments with a higher number of bits will result in a greater level of security. However, it will come at the cost of increased computation time. The default values of SHA1 hashes and 2048 bit prime numbers strike a good balance between performance and security. These values should be sufficient for most applications. Regardless of which values are used, the parameters passed to the *User* and *Verifier* constructors must exactly match those passed to *create_salted_verification_key()* .. _constants: Constants --------- .. table:: Hashing Algorithm Constants ============== ============== Hash Algorithm Number of Bits ============== ============== SHA1 160 SHA224 224 SHA256 256 SHA384 384 SHA512 512 ============== ============== .. note:: Larger hashing algorithms will result in larger session keys. .. table:: Prime Number Constants ================= ============== Prime Number Size Number of Bits ================= ============== NG_1024 1024 NG_2048 2048 NG_4096 4096 NG_8192 8192 NG_CUSTOM User Supplied ================= ============== .. note:: If NG_CUSTOM is used, the 'n_hex' and 'g_hex' parameters are required. These parameters must be ASCII text containing hexidecimal notation of the prime number 'n_hex' and the corresponding generator number 'g_hex'. Appendix A of RFC 5054 contains several large prime number, generator pairs that may be used with NG_CUSTOM. Functions --------- .. function:: create_salted_verification_key ( username, password[, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None] ) *username* Name of the user *password* Plaintext user password *hash_alg*, *ng_type*, *n_hex*, *g_hex* Refer to the :ref:`constants` section. Generate a salted verification key for the given username and password and return the tuple: (salt_bytes, verification_key_bytes) :class:`Verifier` Objects ------------------------- A :class:`Verifier` object is used to verify the identity of a remote user. .. note:: The standard SRP 6 protocol allows only one password attempt per connection. .. class:: Verifier( username, bytes_s, bytes_v, bytes_A[, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None] ) *username* Name of the remote user being authenticated. *bytes_s* Salt generated by :func:`create_salted_verification_key`. *bytes_v* Verification Key generated by :func:`create_salted_verification_key`. *bytes_A* Challenge from the remote user. Generated by :meth:`User.start_authentication` *hash_alg*, *ng_type*, *n_hex*, *g_hex* Refer to the :ref:`constants` section. .. method:: Verifier.authenticated() Return True if the authentication succeeded. False otherwise. .. method:: Verifier.get_username() Return the name of the user this :class:`Verifier` object is for. .. method:: Verifier.get_session_key() Return the session key for an authenticated user or None if the authentication failed or has not yet completed. .. method:: Verifier.get_challenge() Return (bytes_s, bytes_B) on success or (None, None) if authentication has failed. .. method:: Verifier.verify_session( user_M ) Complete the :class:`Verifier` side of the authentication process. If the authentication succeded the return result, bytes_H_AMK should be returned to the remote user. On failure, this method returns None. :class:`User` Objects ------------------------- A :class:`User` object is used to prove a user's identity to a remote :class:`Verifier` and verifiy that the remote :class:`Verifier` knows the verification key associated with the user's password. .. class:: User( username, password[, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None] ) *username* Name of the user being authenticated. *password* Password for the user. *hash_alg*, *ng_type*, *n_hex*, *g_hex* Refer to the :ref:`constants` section. .. method:: User.authenticated() Return True if authentication succeeded. False otherwise. .. method:: User.get_username() Return the username passed to the constructor. .. method:: User.get_session_key() Return the session key if authentication succeeded or None if the authentication failed or has not yet completed. .. method:: User.start_authentication() Return (username, bytes_A). These should be passed to the constructor of the remote :class:`Verifer` .. method:: User.process_challenge( bytes_s, bytes_B ) Processe the challenge returned by :meth:`Verifier.get_challenge` on success this method returns bytes_M that should be sent to :meth:`Verifier.verify_session` if authentication failed, it returns None. .. method:: User.verify_session( bytes_H_AMK ) Complete the :class:`User` side of the authentication process. By verifying the *bytes_H_AMK* value returned by :meth:`Verifier.verify_session`. If the authentication succeded :meth:`authenticated` will return True .. _example: Example ------- Simple Usage Example:: import srp # The salt and verifier returned from srp.create_salted_verification_key() should be # stored on the server. salt, vkey = srp.create_salted_verification_key( 'testuser', 'testpassword' ) class AuthenticationFailed (Exception): pass # ~~~ Begin Authentication ~~~ usr = srp.User( 'testuser', 'testpassword' ) uname, A = usr.start_authentication() # The authentication process can fail at each step from this # point on. To comply with the SRP protocol, the authentication # process should be aborted on the first failure. # Client => Server: username, A svr = srp.Verifier( uname, salt, vkey, A ) s,B = svr.get_challenge() if s is None or B is None: raise AuthenticationFailed() # Server => Client: s, B M = usr.process_challenge( s, B ) if M is None: raise AuthenticationFailed() # Client => Server: M HAMK = svr.verify_session( M ) if HAMK is None: raise AuthenticationFailed() # Server => Client: HAMK usr.verify_session( HAMK ) # At this point the authentication process is complete. assert usr.authenticated() assert svr.authenticated() Implementation Notes -------------------- This implementation of SRP consists of both a pure-python module and a C-based implementation that is approximately 10x faster. By default, the C-implementation will be used if it is available. An additional benefit of the C implementation is that it can take advantage of of multiple CPUs. For cases in which the number of connections per second is an issue, using a small pool of threads to perform the authentication steps on multi-core systems will yield a substantial performance increase. .. _protocol-description: SRP 6a Protocol Description --------------------------- The original SRP protocol, known as SRP-3, is defined in RFC 2945. This implementation, however, uses SRP-6a which is a slight improvement over SRP-3. The authoritative definition for the SRP-6a protocol is available at http://srp.stanford.edu. An additional resource is RFC 5054 which covers the integration of SRP into TLS. This RFC is the source of hashing strategy and the predefined N and g constants used in this implementation. The following is a complete description of the SRP-6a protocol as implemented by this library. Note that the ^ symbol indicates exponentiaion and the | symbol indicates concatenation. .. rubric:: Primary Variables used in SRP 6a ========= ================================================================= Variables Description ========= ================================================================= N A large, safe prime (N = 2q+1, where q is a Sophie Germain prime) All arithmetic is performed in the field of integers modulo N g A generator modulo N s Small salt for the verification key I Username p Cleartext password H() One-way hash function a,b Secret, random values K Session key ========= ================================================================= .. rubric:: Derived Values used in SRP 6a ====================================== ==================================== Derived Values Description ====================================== ==================================== k = H(N,g) Multiplier Parameter A = g^a Public ephemeral value B = kv + g^b Public ephemeral value x = H(s, H( I | ':' | p )) Private key (as defined by RFC 5054) v = g^x Password verifier u = H(A,B) Random scrambling parameter M = H(H(N) xor H(g), H(I), s, A, B, K) Session key verifier ====================================== ==================================== .. rubric:: Protocol Description The server stores the password verifier *v*. Authentication begins with a message from the client:: client -> server: I, A = g^a The server replies with the verifier salt and challenge:: server -> client: s, B = kv + g^b At this point, both the client and server calculate the shared session key:: client & server: u = H(A,B) :: server: K = H( (Av^u) ^ b ) :: client: x = H( s, H( I + ':' + p ) ) client: K = H( (B - kg^x) ^ (a + ux) ) Now both parties have a shared, strong session key *K*. To complete authentication they need to prove to each other that their keys match:: client -> server: M = H(H(N) xor H(g), H(I), s, A, B, K) server -> client: H(A, M, K) SRP 6a requires the two parties to use the following safeguards: 1. The client will abort if it recieves B == 0 (mod N) or u == 0 2. The server will abort if it detects A == 0 (mod N) 3. The client must show its proof of K first. If the server detects that this proof is incorrect it must abort without showing its own proof of K pysrp-1.0.4/srp/test_srp.py000066400000000000000000000170161221043136700157160ustar00rootroot00000000000000#!/usr/bin/env python import unittest import os.path import os import sys import time import thread this_dir = os.path.dirname( os.path.abspath(__file__) ) build_dir = os.path.join( os.path.dirname(this_dir), 'build' ) if not os.path.exists( build_dir ): print 'Please run "python setup.py build" prior to running tests' sys.exit(1) plat_dirs = [ d for d in os.listdir('build') if d.startswith('lib') ] if not len(plat_dirs) == 1: print 'Unexpected build result... aborting' plat_dir = os.path.join( build_dir, plat_dirs[0] ) sys.path.insert(0, os.path.join('build', plat_dir) ) import srp import srp._pysrp as _pysrp import srp._ctsrp as _ctsrp try: import srp._srp as _srp except ImportError: print 'Failed to import srp._srp. Aborting tests' sys.exit(1) test_g_hex = "2" test_n_hex = '''\ AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4\ A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60\ 95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF\ 747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907\ 8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861\ 60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB\ FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73''' class SRPTests( unittest.TestCase ): def doit(self, u_mod, v_mod, g_mod, hash_alg=srp.SHA1, ng_type=srp.NG_2048, n_hex='', g_hex=''): User = u_mod.User Verifier = v_mod.Verifier create_salted_verification_key = g_mod.create_salted_verification_key username = 'testuser' password = 'testpassword' _s, _v = create_salted_verification_key( username, password, hash_alg, ng_type, n_hex, g_hex ) usr = User( username, password, hash_alg, ng_type, n_hex, g_hex ) uname, A = usr.start_authentication() # username, A => server svr = Verifier( uname, _s, _v, A, hash_alg, ng_type, n_hex, g_hex ) s,B = svr.get_challenge() # s,B => client M = usr.process_challenge( s, B ) # M => server HAMK = svr.verify_session( M ) # HAMK => client usr.verify_session( HAMK ) self.assertTrue( svr.authenticated() and usr.authenticated() ) def test_pure_python_defaults(self): self.doit( _pysrp, _pysrp, _pysrp ) def test_ctypes_defaults(self): self.doit( _ctsrp, _ctsrp, _ctsrp ) def test_c_defaults(self): self.doit( _srp, _srp, _srp ) def test_mix1(self): self.doit( _pysrp, _ctsrp, _srp ) def test_mix2(self): self.doit( _pysrp, _srp, _ctsrp ) def test_mix3(self): self.doit( _ctsrp, _pysrp, _srp ) def test_mix4(self): self.doit( _ctsrp, _srp, _pysrp ) def test_mix5(self): self.doit( _srp, _pysrp, _ctsrp ) def test_mix6(self): self.doit( _srp, _ctsrp, _pysrp ) def test_hash_SHA512(self): self.doit( _srp, _srp, _srp, hash_alg=srp.SHA512 ) def test_NG_8192(self): self.doit( _srp, _srp, _srp, ng_type=srp.NG_8192 ) def test_NG_CUSTOM(self): self.doit( _srp, _srp, _srp, ng_type=srp.NG_CUSTOM, n_hex=test_n_hex, g_hex=test_g_hex ) def test_all1(self): self.doit( _srp, _pysrp, _ctsrp, hash_alg=srp.SHA256, ng_type=srp.NG_CUSTOM, n_hex=test_n_hex, g_hex=test_g_hex ) def test_all2(self): self.doit( _ctsrp, _pysrp, _srp, hash_alg=srp.SHA224, ng_type=srp.NG_4096 ) def test_authenticated_on_init(self): usr = _pysrp.User('test', 'test') self.assertTrue(not usr.authenticated()) usr = _ctsrp.User('test', 'test') self.assertTrue(not usr.authenticated()) usr = _srp.User('test', 'test') self.assertTrue(not usr.authenticated()) #----------------------------------------------------------------------------------- # Performance Testing # hash_map = { 0 : 'SHA1 ', 1 : 'SHA224', 2 : 'SHA256', 3 : 'SHA384', 4 : 'SHA512' } prime_map = { 0 : 1024, 1 : 2048, 2 : 4096, 3 : 8192 } username = 'testuser' password = 'testpassword' NLEFT = 0 def do_auth( mod, hash_alg, ng_type, _s, _v ): usr = mod.User( username, password, hash_alg, ng_type) uname, A = usr.start_authentication() # username, A => server svr = mod.Verifier( uname, _s, _v, A, hash_alg, ng_type) s,B = svr.get_challenge() # s,B => client M = usr.process_challenge( s, B ) # M => server HAMK = svr.verify_session( M ) # HAMK => client usr.verify_session( HAMK ) if not svr.authenticated() or not usr.authenticated(): raise Exception('Authentication failed!') def performance_test( mod, hash_alg, ng_type, niter=10, nthreads=1 ): global NLEFT _s, _v = srp.create_salted_verification_key( username, password, hash_alg, ng_type ) NLEFT = niter def test_thread(): global NLEFT while NLEFT > 0: do_auth( mod, hash_alg, ng_type, _s, _v ) NLEFT -= 1 start = time.time() while nthreads > 1: thread.start_new_thread( test_thread, () ) nthreads -= 1 test_thread() duration = time.time() - start return duration def get_param_str( mod, hash_alg, ng_type ): m = { 'srp._pysrp' : 'Python', 'srp._ctsrp' : 'ctypes', 'srp._srp' : 'C ' } cfg = '%s, %s, %d:' % (m[mod.__name__], hash_map[hash_alg], prime_map[ng_type]) return cfg def param_test( mod, hash_alg, ng_type, niter=10 ): duration = performance_test( mod, hash_alg, ng_type, niter ) cfg = get_param_str( mod, hash_alg, ng_type ) print ' ', cfg.ljust(20), '%.6f' % (duration/niter) return duration/niter def print_default_timings(): print '*'*60 print 'Default Parameter Timings:' py_time = param_test( _pysrp, srp.SHA1, srp.NG_2048 ) ct_time = param_test( _ctsrp, srp.SHA1, srp.NG_2048 ) c_time = param_test( _srp, srp.SHA1, srp.NG_2048 ) print '' print 'Performance increases: ' print ' ctypes-module : ', py_time/ct_time print ' C-module : ', py_time/c_time def print_performance_table(): ng_types = [ srp.NG_1024, srp.NG_2048, srp.NG_4096, srp.NG_8192 ] hash_types = [ srp.SHA1, srp.SHA224, srp.SHA256, srp.SHA384, srp.SHA512 ] print '*'*60 print 'Hash Algorithm vs Prime Number performance table' print '' print ' |', for ng in ng_types: print ('NG_%d' % prime_map[ng]).rjust(12), print '' print '-'*60 for hash_alg in hash_types: print '%s |' % hash_map[hash_alg], for ng in ng_types: print '{0:>12f}'.format(performance_test(_srp, hash_alg, ng) / 10), print '' def print_thread_performance(): print '*'*60 print 'Thread Performance Test:' niter = 100 for nthreads in range(1,11): print ' Thread Count {0:>2}: {1:8f}'.format(nthreads, performance_test(_srp, srp.SHA1, srp.NG_2048, niter, nthreads)/niter) print '*'*60 print '*' print '* Testing Implementation' print '*' suite = unittest.TestLoader().loadTestsFromTestCase(SRPTests) unittest.TextTestRunner(verbosity=1).run(suite) print '*'*60 print '*' print '* Performance Testing' print '*' print_thread_performance() print_performance_table() print_default_timings() #--------------------------------------------------------------- # Pause briefly to ensure no background threads are still executing time.sleep(0.1)