1 | # -*- coding: utf-8 -*- |
---|
2 | """ |
---|
3 | pbkdf2 |
---|
4 | ~~~~~~ |
---|
5 | |
---|
6 | This module implements pbkdf2 for Python. It also has some basic |
---|
7 | tests that ensure that it works. The implementation is straightforward |
---|
8 | and uses stdlib only stuff and can be easily be copy/pasted into |
---|
9 | your favourite application. |
---|
10 | |
---|
11 | Use this as replacement for bcrypt that does not need a c implementation |
---|
12 | of a modified blowfish crypto algo. |
---|
13 | |
---|
14 | Example usage: |
---|
15 | |
---|
16 | >>> pbkdf2_hex('what i want to hash', 'the random salt') |
---|
17 | 'fa7cc8a2b0a932f8e6ea42f9787e9d36e592e0c222ada6a9' |
---|
18 | |
---|
19 | How to use this: |
---|
20 | |
---|
21 | 1. Use a constant time string compare function to compare the stored hash |
---|
22 | with the one you're generating:: |
---|
23 | |
---|
24 | def safe_str_cmp(a, b): |
---|
25 | if len(a) != len(b): |
---|
26 | return False |
---|
27 | rv = 0 |
---|
28 | for x, y in izip(a, b): |
---|
29 | rv |= ord(x) ^ ord(y) |
---|
30 | return rv == 0 |
---|
31 | |
---|
32 | 2. Use `os.urandom` to generate a proper salt of at least 8 byte. |
---|
33 | Use a unique salt per hashed password. |
---|
34 | |
---|
35 | 3. Store ``algorithm$salt:costfactor$hash`` in the database so that |
---|
36 | you can upgrade later easily to a different algorithm if you need |
---|
37 | one. For instance ``PBKDF2-256$thesalt:10000$deadbeef...``. |
---|
38 | |
---|
39 | |
---|
40 | :copyright: (c) Copyright 2011 by Armin Ronacher. |
---|
41 | :license: BSD, see LICENSE for more details. |
---|
42 | """ |
---|
43 | import hmac |
---|
44 | import hashlib |
---|
45 | from struct import Struct |
---|
46 | from operator import xor |
---|
47 | from itertools import izip, starmap |
---|
48 | |
---|
49 | |
---|
50 | _pack_int = Struct('>I').pack |
---|
51 | |
---|
52 | |
---|
53 | def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None): |
---|
54 | """Like :func:`pbkdf2_bin` but returns a hex encoded string.""" |
---|
55 | return pbkdf2_bin(data, salt, iterations, keylen, hashfunc).encode('hex') |
---|
56 | |
---|
57 | |
---|
58 | def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): |
---|
59 | """Returns a binary digest for the PBKDF2 hash algorithm of `data` |
---|
60 | with the given `salt`. It iterates `iterations` time and produces a |
---|
61 | key of `keylen` bytes. By default SHA-1 is used as hash function, |
---|
62 | a different hashlib `hashfunc` can be provided. |
---|
63 | """ |
---|
64 | hashfunc = hashfunc or hashlib.sha1 |
---|
65 | mac = hmac.new(data, None, hashfunc) |
---|
66 | def _pseudorandom(x, mac=mac): |
---|
67 | h = mac.copy() |
---|
68 | h.update(x) |
---|
69 | return map(ord, h.digest()) |
---|
70 | buf = [] |
---|
71 | for block in xrange(1, -(-keylen // mac.digest_size) + 1): |
---|
72 | rv = u = _pseudorandom(salt + _pack_int(block)) |
---|
73 | for i in xrange(iterations - 1): |
---|
74 | u = _pseudorandom(''.join(map(chr, u))) |
---|
75 | rv = starmap(xor, izip(rv, u)) |
---|
76 | buf.extend(rv) |
---|
77 | return ''.join(map(chr, buf))[:keylen] |
---|
78 | |
---|
79 | |
---|
80 | def test(): |
---|
81 | failed = [] |
---|
82 | def check(data, salt, iterations, keylen, expected): |
---|
83 | rv = pbkdf2_hex(data, salt, iterations, keylen) |
---|
84 | if rv != expected: |
---|
85 | print('Test failed:') |
---|
86 | print(' Expected: %s' % expected) |
---|
87 | print(' Got: %s' % rv) |
---|
88 | print(' Parameters:') |
---|
89 | print(' data=%s' % data) |
---|
90 | print(' salt=%s' % salt) |
---|
91 | print(' iterations=%d' % iterations) |
---|
92 | print() |
---|
93 | failed.append(1) |
---|
94 | |
---|
95 | # From RFC 6070 |
---|
96 | check('password', 'salt', 1, 20, |
---|
97 | '0c60c80f961f0e71f3a9b524af6012062fe037a6') |
---|
98 | check('password', 'salt', 2, 20, |
---|
99 | 'ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957') |
---|
100 | check('password', 'salt', 4096, 20, |
---|
101 | '4b007901b765489abead49d926f721d065a429c1') |
---|
102 | check('passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt', |
---|
103 | 4096, 25, '3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038') |
---|
104 | check('pass\x00word', 'sa\x00lt', 4096, 16, |
---|
105 | '56fa6aa75548099dcc37d7f03425e0c3') |
---|
106 | # This one is from the RFC but it just takes for ages |
---|
107 | ##check('password', 'salt', 16777216, 20, |
---|
108 | ## 'eefe3d61cd4da4e4e9945b3d6ba2158c2634e984') |
---|
109 | |
---|
110 | # From Crypt-PBKDF2 |
---|
111 | check('password', 'ATHENA.MIT.EDUraeburn', 1, 16, |
---|
112 | 'cdedb5281bb2f801565a1122b2563515') |
---|
113 | check('password', 'ATHENA.MIT.EDUraeburn', 1, 32, |
---|
114 | 'cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837') |
---|
115 | check('password', 'ATHENA.MIT.EDUraeburn', 2, 16, |
---|
116 | '01dbee7f4a9e243e988b62c73cda935d') |
---|
117 | check('password', 'ATHENA.MIT.EDUraeburn', 2, 32, |
---|
118 | '01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86') |
---|
119 | check('password', 'ATHENA.MIT.EDUraeburn', 1200, 32, |
---|
120 | '5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13') |
---|
121 | check('X' * 64, 'pass phrase equals block size', 1200, 32, |
---|
122 | '139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1') |
---|
123 | check('X' * 65, 'pass phrase exceeds block size', 1200, 32, |
---|
124 | '9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a') |
---|
125 | |
---|
126 | raise SystemExit(bool(failed)) |
---|
127 | |
---|
128 | |
---|
129 | if __name__ == '__main__': |
---|
130 | test() |
---|