2008-01-12

Seeded (Salted) SHA Passwords

This week I needed to work with passwords from an OpenLDAP database. I needed to create users and encode their passwords as SSHA. After much googling and reading of authentication code examples, this is what I came up with.


import hashlib
import os
from base64 import urlsafe_b64encode as encode
from base64 import urlsafe_b64decode as decode

def makeSecret(password):
salt = os.urandom(4)
h = hashlib.sha1(password)
h.update(salt)
return "{SSHA}" + encode(h.digest() + salt)

def checkPassword(challenge_password, password):
challenge_bytes = decode(challenge_password[6:])
digest = challenge_bytes[:20]
salt = challenge_bytes[20:]
hr = hashlib.sha1(password)
hr.update(salt)
return digest == hr.digest()

Wow, python rules. So first I want to make sure I can validate a password against one generated by slappasswd

reedobrien$ slappasswd -s topsecret
{SSHA}Ccpjsip2UZL2CR2VsWTH7aF0vWKHQ7jn
And then see if I can validate it

>>> checkPassword('{SSHA}Ccpjsip2UZL2CR2VsWTH7aF0vWKHQ7jn',
... 'topsecret')
True

Next I need to make sure I can generate one that OpenLDAP can use. So I used a script I wrote to create a user in ldap. This time I use the same password and get a different hash which is good.

>>> pw = "{SSHA}hq5ROYE8RoLA1Zcz6azgNQP5PkdETocx"

which OpenLDAP represents in LDIFs as base64 encoded

...
# tester, people, reedobrien.com
dn: uid=tester,ou=people,dc=reedobrien,dc=com
objectClass: top
objectClass: inetOrgPerson
uid: tester
cn: test test
sn: test
mail: test@example.com
userPassword:: e1NTSEF9MHhaMEdZc2Fob1JNeXZWR2FVdGszS0VwSFZTQnVLTlc=
...

So I try authenticating:

>>> userPasword = "e1NTSEF9MHhaMEdZc2Fob1JNeXZWR2FVdGszS0VwSFZTQnVLTlc="

>>> decode(userPassword) == pw
True

>>> checkPassword(decode(userPassword), 'topsecret')
True

Now I want to make sure it works through the ldap server itself
In python:


>>> import ldap

>>> con = ldap.initialize("ldap://127.0.0.1")
>>> con.simple_bind_s('uid=tester,ou=people,dc=reedbrien,dc=com',
'topsecret')
(97, []) ## indicates success

And not using my stuff.

reedobrien$ ldapsearch -x -D "uid=tester,ou=people,dc=reedobrien,dc=com" -w topsecret
# extended LDIF
#
# LDAPv3
# base <> with scope sub
# filter: (objectclass=*)
# requesting: ALL
#

# search result
search: 2
result: 32 No such object

# numResponses: 1


How do they say, `w00t!`

So it was cool doing it and it made me feel smart for a minute. I am sure that there is a module or ten out there that already does this, but I couldn't find it. At least it was educational.

Unfortunately, after getting it to work; I don't I understand any better what it really does or how SSHA is better than plain old SHA.

Oh, well. Hopefully it helps some weary traveller out there one day.

1 comment:

Anonymous said...

Salted SHA1 is preferred for password storage because it prevents (or at least makes exponentially more difficult) dictionay attacks on the passwords.

http://en.wikipedia.org/wiki/Dictionary_attack