Benjamin2002
Benjamin2002

Reputation: 331

Pbkdf2_sha256 encryption

Hello everyone so I'm figuring out how Pbkdf2_sha256 works.

Here are some of the cracked hashes I'm currently studying

PBKDF2 pbkdf2_sha256$10000$005OtPxTXhPq$K/2GplWPJsBVj+qbgdKW8YEteQyUkIiquT5MaOhPo4Y=:harry
PBKDF2 pbkdf2_sha256$10000$00Qhibr5Mbeg$l9grYueDrl3qN3NA7e9j5PodgV1XkGTz0Z6ajhF99AY=:radio
PBKDF2 pbkdf2_sha256$10000$00h7h0g1ZKE1$YEobSm/y+cFg/VXhU4gGYJ6eOkZ68jhJ5axDu68Dack=:momo
PBKDF2 pbkdf2_sha256$10000$01JMkfGk1RXh$vD+GGZshw5kExtZOpl5+Lht3xECULdbNVOesoTicxto=:fred
PBKDF2 pbkdf2_sha256$10000$01vkw1viCg4J$2hjlbq10Jh/Su3yqjKfYCnCSt1WlKcKJtsqDET618M0=:get
PBKDF2 pbkdf2_sha256$10000$01wayF5JLVSZ$2/9COWqb6SZG/raqabtU8fNBzkrt2puN7SaKw0U7jBs=:987456321

And here is my code and output for calculating the hash

>>> from passlib.hash import pbkdf2_sha256
>>> from passlib.utils.binary import ab64_decode
>>> print(pbkdf2_sha256.hash("harry", rounds=10000, salt=ab64_decode(b'005OtPxTXhPq')))
$pbkdf2-sha256$10000$005OtPxTXhPq$l9LhRMPBW.EEdlBE9b.P0Z70Kxidl9EJhfGK7FiLUHA

Comparing these two and you can see a difference.

$pbkdf2_sha256$10000$005OtPxTXhPq$K/2GplWPJsBVj+qbgdKW8YEteQyUkIiquT5MaOhPo4Y=
$pbkdf2-sha256$10000$005OtPxTXhPq$l9LhRMPBW.EEdlBE9b.P0Z70Kxidl9EJhfGK7FiLUHA

Can someone please explain what causes this and how can I calculate the correct hash?

Thanks in advance!

Upvotes: 4

Views: 18539

Answers (2)

That format is used by Django and passlib have functions for that format: Django 1.4 Hashes.

So, I did this in python's shell:

>>> from passlib.hash import django_pbkdf2_sha256
>>> secret = 'harry'
>>> hash = 'pbkdf2_sha256$10000$005OtPxTXhPq$K/2GplWPJsBVj+qbgdKW8YEteQyUkIiquT5MaOhPo4Y='
>>> django_pbkdf2_sha256.verify(secret, hash)
True
>>> rounds = hash.split('$')[1]
>>> salt = hash.split('$')[2]
>>> django_pbkdf2_sha256.hash(secret, rounds=rounds, salt=salt) == hash
True

Upvotes: 3

Topaco
Topaco

Reputation: 49241

As already mentioned in the comment, the posted data has a format different from passlib: The passlib format is explained here. Salt and hash (checksum) are Base64 encoded. A special Base64 variant is used that is explained here: Padding (=) and whitespaces are omitted and . is applied instead +.

The hash of the posted data on the other hand is standard Base64 encoded (i.e. with + instead of .) and with padding (=). Furthermore the salt is UTF8 decoded.

If this is taken into account the salts and hashes are identical. The following code determines the passlib data from the posted data and compares salt and hash, where salt and hash of the posted data are displayed in passlib format (i.e. with the passlib Base64 variant and Base64 encoded salt):

from passlib.hash import pbkdf2_sha256
from base64 import b64decode
from passlib.utils.binary import ab64_encode

def hashAndCompare(crackedHash):
    
    crackedChain = crackedHash.split('$')   
    #crackedChainDigest = crackedChain[0]
    crackedChainRounds = crackedChain[1]
    crackedChainSalt = crackedChain[2]
    crackedChainSaltPasslibFormat = ab64_encode(crackedChainSalt.encode('utf8')).decode('utf8')
    crackedChainHashData = crackedChain[3].split(':')
    crackedChainHash = crackedChainHashData[0]
    crackedChainHashPasslibFormat = ab64_encode(b64decode(crackedChainHash)).decode('utf8')
    crackedChainData = crackedChainHashData[1]
    
    passlibHash = pbkdf2_sha256.hash(crackedChainData, rounds=crackedChainRounds, salt=crackedChainSalt.encode('utf8')) 
    passlibChain = passlibHash.split('$')
    passlibChainSalt = passlibChain[3]
    passlibChainHash = passlibChain[4]
    
    print('Passlib: Hash: {0} Salt: {1}\nCracked: Hash: {2} Salt: {3}\n'.format(passlibChainHash, passlibChainSalt, crackedChainHashPasslibFormat, crackedChainSaltPasslibFormat))

hashAndCompare('pbkdf2_sha256$10000$005OtPxTXhPq$K/2GplWPJsBVj+qbgdKW8YEteQyUkIiquT5MaOhPo4Y=:harry')
hashAndCompare('pbkdf2_sha256$10000$00Qhibr5Mbeg$l9grYueDrl3qN3NA7e9j5PodgV1XkGTz0Z6ajhF99AY=:radio')
hashAndCompare('pbkdf2_sha256$10000$00h7h0g1ZKE1$YEobSm/y+cFg/VXhU4gGYJ6eOkZ68jhJ5axDu68Dack=:momo')
hashAndCompare('pbkdf2_sha256$10000$01JMkfGk1RXh$vD+GGZshw5kExtZOpl5+Lht3xECULdbNVOesoTicxto=:fred')
hashAndCompare('pbkdf2_sha256$10000$01vkw1viCg4J$2hjlbq10Jh/Su3yqjKfYCnCSt1WlKcKJtsqDET618M0=:get')
hashAndCompare('pbkdf2_sha256$10000$01wayF5JLVSZ$2/9COWqb6SZG/raqabtU8fNBzkrt2puN7SaKw0U7jBs=:987456321')

Salts and hashes are identical with consistent encoding:

Passlib: Hash: K/2GplWPJsBVj.qbgdKW8YEteQyUkIiquT5MaOhPo4Y Salt: MDA1T3RQeFRYaFBx
Cracked: Hash: K/2GplWPJsBVj.qbgdKW8YEteQyUkIiquT5MaOhPo4Y Salt: MDA1T3RQeFRYaFBx

Passlib: Hash: l9grYueDrl3qN3NA7e9j5PodgV1XkGTz0Z6ajhF99AY Salt: MDBRaGlicjVNYmVn
Cracked: Hash: l9grYueDrl3qN3NA7e9j5PodgV1XkGTz0Z6ajhF99AY Salt: MDBRaGlicjVNYmVn

Passlib: Hash: YEobSm/y.cFg/VXhU4gGYJ6eOkZ68jhJ5axDu68Dack Salt: MDBoN2gwZzFaS0Ux
Cracked: Hash: YEobSm/y.cFg/VXhU4gGYJ6eOkZ68jhJ5axDu68Dack Salt: MDBoN2gwZzFaS0Ux

Passlib: Hash: vD.GGZshw5kExtZOpl5.Lht3xECULdbNVOesoTicxto Salt: MDFKTWtmR2sxUlho
Cracked: Hash: vD.GGZshw5kExtZOpl5.Lht3xECULdbNVOesoTicxto Salt: MDFKTWtmR2sxUlho

Passlib: Hash: 2hjlbq10Jh/Su3yqjKfYCnCSt1WlKcKJtsqDET618M0 Salt: MDF2a3cxdmlDZzRK
Cracked: Hash: 2hjlbq10Jh/Su3yqjKfYCnCSt1WlKcKJtsqDET618M0 Salt: MDF2a3cxdmlDZzRK

Passlib: Hash: 2/9COWqb6SZG/raqabtU8fNBzkrt2puN7SaKw0U7jBs Salt: MDF3YXlGNUpMVlNa
Cracked: Hash: 2/9COWqb6SZG/raqabtU8fNBzkrt2puN7SaKw0U7jBs Salt: MDF3YXlGNUpMVlNa

Upvotes: 5

Related Questions