On 02/20/2017 01:51 PM, Aleksander Alekseev wrote:
Currently I don't see any significant flaws in these patches. However I
would like to verify that implemented algorithms are compatible with
algorithms implemented by third party.

Yes, that's very important.

For instance, for user 'eax' and password 'pass' I got the following
record in pg_authid:

```
scram-sha-256:
xtznkRN/nc/1DQ==:
4096:
2387c124a3139a276b848c910f43ece05dd670d0977ace4f20d724af522312e4:
5e3bdd6584880198b0375acabd8754c460af2389499f71a756660a10a8aaa843
```

Let's say I would like to implement SCRAM in pure Python, for instance
add it to pg8000 driver. Firstly I need to know how to generate server
key and client key having only user name and password. Somehow like
this:

```
 >>> import base64
 >>> import hashlib
 >>> base64.b16encode(hashlib.pbkdf2_hmac('sha256', b'pass',
 ...    base64.b64decode(b'xtznkRN/nc/1DQ=='), 4096))
b'14B90CFCF690120399A0E6D30C60DD9D9D221CD9C2E31EA0A00514C41823E6C3'
```

Naturally it doesn't work because both SCRAM_SERVER_KEY_NAME and
SCRAM_CLIENT_KEY_NAME should also be involved. I see PostgreSQL
implementation just in front of me but unfortunately I'm still having
problems calculating exactly the same server key and client key. It makes
me worry whether PostgreSQL implementation is OK.

Could you please give an example of how to do it?

RFC5802 describes the protocol in detail:

     SaltedPassword  := Hi(Normalize(password), salt, i)
     ClientKey       := HMAC(SaltedPassword, "Client Key")
     StoredKey       := H(ClientKey)
     AuthMessage     := client-first-message-bare + "," +
                        server-first-message + "," +
                        client-final-message-without-proof
     ClientSignature := HMAC(StoredKey, AuthMessage)
     ClientProof     := ClientKey XOR ClientSignature
     ServerKey       := HMAC(SaltedPassword, "Server Key")
     ServerSignature := HMAC(ServerKey, AuthMessage)

You've calculated SaltedPassword correctly with your Python snippet. To derive ClientKey from it, you need to pass it to the HMAC() function. In python, that'd be hmac.new(SaltedPassword, "Client Key", hashlib.sha256). For example:

```
import base64
import hashlib
import hmac

salt = base64.b64decode(b'xtznkRN/nc/1DQ==');
SaltedPassword = hashlib.pbkdf2_hmac('sha256', b'pass',
                                     salt, 4096);
ClientKey = hmac.new(SaltedPassword, "Client Key", hashlib.sha256).hexdigest()
print 'SaltedPassword: ' + base64.b16encode(SaltedPassword)
print 'ClientKey; ' + ClientKey
```

This prints:

SaltedPassword: 14B90CFCF690120399A0E6D30C60DD9D9D221CD9C2E31EA0A00514C41823E6C3
ClientKey; 5b681195146a2027cb028a921bd0a89ff858b74bd2b38ed8b42561c28b1e369f

Which matches what the libpq implementation calculated. For constructing the whole client-final-message, you'll also need to calculate ClientSignature and ClientProof, which depend on the nonces, and is therefore different on every authentication exchange.

- Heikki



--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to