cocagne / pysrp

Python implementation of the Secure Remote Password protocol (SRP)
MIT License
113 stars 42 forks source link

Make more 'transactional' #6

Open nelz9999 opened 9 years ago

nelz9999 commented 9 years ago

I'm trying to use this library in a Web context... I would prefer if at least the Verifier-side wasn't required to be long-lived.

Here's an example of the unit test rewritten to show what I'm talking about:

import srp

        # The salt and verifier returned from srp.create_salted_verification_key() should be
        # stored on the server.
        username = 'testuser'
        password = 'testpassword'
        salt, vkey = srp.create_salted_verification_key(username, password)

        class AuthenticationFailed (Exception):
            pass

        # ~~~ Begin Authentication ~~~
        usr      = srp.User(username, password)
        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
        svr      = srp.Verifier(uname, salt, vkey, A)
        HAMK     = svr.verify_session( M )
        if HAMK is None:
            raise AuthenticationFailed()

        private_b = srv.get_private_b()
        svr2    = srp.Verifier(uname, salt, vkey, A, private_b=private_b)
        HAMK2   = svr2.verify_session( M )
        if HAMK2 is None:
            raise AuthenticationFailed()

        # Server => Client: HAMK
        usr.verify_session( HAMK )
        usr.verify_session( HAMK2 )

        # At this point the authentication process is complete.

        assert usr.authenticated()
        assert svr.authenticated()
        assert svr2.authenticated()

Notice that srv2 was recreated from srv. This will enable the web back-end to be short-lived while doing two client->server hits.

cocagne commented 9 years ago

In what way would this allow the verifier portion to be more short lived? Some state must be persisted for the duration of the authentication attempt. On the surface, it would seem that there's little, if any, advantage to that approach. You already have an object reference to the full verifier object, why trade that for an object reference to a sub-component of that object and re-create it later?

nelz9999 commented 9 years ago

Thanks for entertaining my request.

Here's the flow... (I'll use the nomenclature from the doc here: http://srp.stanford.edu/design.html)

At account-setup-time: (Like normal) User sends I and p. I use create_salted_verification_key to create s and v. I store v and s keyed by I.

User now wants to log in: ROUNDTRIP No.1 - Client does HTTP POST to (LoadBalancer->)ServerA: I and A. ServerA creates b and B and a transaction_id. ServerA saves A, I, and b, keyed by transaction_id as a short-lived entry in a data store. RETURN: B, s, and transaction_id.

ROUNDTRIP No.2 - Client does HTTP POST to (LoadBalancer->)ServerB: transaction_id and M. ServerB is able to use transaction_id to find I, s, v, A, and b from the data store, and recreates the srp.Verifier to generate the HAMK. RETURN: HAMK.

This allows me to server the two round-trips via stateless webservers.

cocagne commented 9 years ago

Now I follow you. You're looking to effectively span process boundaries via a backing store. That makes sense and, skimming the code, it looks fairly easy to implement. The only real complicating factor is that pysrp has 3 implementations, plain python, ctypes + openssl, and a built-in python module. All three would need to be updated to support the new keyword-argument to the verifier class.

It's a good idea and shouldn't take more than a handful of hours to complete. I'm pretty tied down at the moment though. I'll put it on my queue for eventual implementation but it could be a while before I get around to it. If you'd like to take a crack at it yourself, I'd gladly accept a merge request.

Thanks for the suggestion.

Tom

On Wed, Sep 3, 2014 at 11:02 PM, Nelz notifications@github.com wrote:

Thanks for entertaining my request.

Here's the flow... (I'll use the nomenclature from the doc here: http://srp.stanford.edu/design.html)

At account-setup-time: (Like normal) User sends I and p. I use create_salted_verification_key to create s and v. I store v and s keyed by I.

User now wants to log in: ROUNDTRIP No.1 - Client does HTTP POST to (LoadBalancer->)ServerA: I and A. ServerA creates b and B and a transaction_id. ServerA saves A, I, and b, keyed by transaction_id as a short-lived entry in a data store. RETURN: B, s, and transaction_id.

ROUNDTRIP No.2 - Client does HTTP POST to (LoadBalancer->)ServerB: transaction_id and M. ServerB is able to use transaction_id to find I, s, v, A, and b from the data store, and recreates the srp.Verifier to generate the HAMK. RETURN: HAMK.

This allows me to server the two round-trips via stateless webservers.

— Reply to this email directly or view it on GitHub https://github.com/cocagne/pysrp/issues/6#issuecomment-54403255.

nelz9999 commented 9 years ago

I should have something for you in a couple of days.