2 # -*- coding: ascii -*-
3 ###########################################################################
4 # PBKDF2.py - PKCS#5 v2.0 Password-Based Key Derivation
6 # Copyright (C) 2007, 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
9 # Permission to use, copy, modify, and distribute this software and its
10 # documentation for any purpose and without fee is hereby granted,
11 # provided that the above copyright notice appear in all copies and that
12 # both that copyright notice and this permission notice appear in
13 # supporting documentation.
15 # THE AUTHOR PROVIDES THIS SOFTWARE ``AS IS'' AND ANY EXPRESSED OR
16 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 # Country of origin: Canada
28 ###########################################################################
29 # Sample PBKDF2 usage:
30 # from Crypto.Cipher import AES
31 # from PBKDF2 import PBKDF2
34 # salt = os.urandom(8) # 64-bit salt
35 # key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
36 # iv = os.urandom(16) # 128-bit IV
37 # cipher = AES.new(key, AES.MODE_CBC, iv)
40 # Sample crypt() usage:
41 # from PBKDF2 import crypt
42 # pwhash = crypt("secret")
43 # alleged_pw = raw_input("Enter password: ")
44 # if pwhash == crypt(alleged_pw, pwhash):
45 # print "Password good"
47 # print "Invalid password"
49 ###########################################################################
52 # 2007-07-27 Dwayne C. Litzenberger <dlitz@dlitz.net>
53 # - Initial Release (v1.0)
55 # 2007-07-31 Dwayne C. Litzenberger <dlitz@dlitz.net>
56 # - Bugfix release (v1.1)
57 # - SECURITY: The PyCrypto XOR cipher (used, if available, in the _strxor
58 # function in the previous release) silently truncates all keys to 64
59 # bytes. The way it was used in the previous release, this would only be
60 # problem if the pseudorandom function that returned values larger than
61 # 64 bytes (so SHA1, SHA256 and SHA512 are fine), but I don't like
62 # anything that silently reduces the security margin from what is
65 # 2008-06-17 Dwayne C. Litzenberger <dlitz@dlitz.net>
66 # - Compatibility release (v1.2)
67 # - Add support for older versions of Python (2.2 and 2.3).
69 ###########################################################################
73 from struct
import pack
74 from binascii
import b2a_hex
75 from random
import randint
79 # Use PyCrypto (if available)
80 from Crypto
.Hash
import HMAC
, SHA
as SHA1
83 # PyCrypto not available. Use the Python standard library.
88 return "".join([chr(ord(x
) ^
ord(y
)) for (x
, y
) in zip(a
, b
)])
90 def b64encode(data
, chars
="+/"):
91 tt
= string
.maketrans("+/", chars
)
92 return data
.encode('base64').replace("\n", "").translate(tt
)
95 """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
97 This implementation takes a passphrase and a salt (and optionally an
98 iteration count, a digest module, and a MAC module) and provides a
99 file-like object from which an arbitrarily-sized key can be read.
101 If the passphrase and/or salt are unicode objects, they are encoded as
102 UTF-8 before they are processed.
104 The idea behind PBKDF2 is to derive a cryptographic key from a
105 passphrase and a salt.
107 PBKDF2 may also be used as a strong salted password hash. The
108 'crypt' function is provided for that purpose.
110 Remember: Keys generated using PBKDF2 are only as strong as the
111 passphrases they are derived from.
114 def __init__(self
, passphrase
, salt
, iterations
=1000,
115 digestmodule
=SHA1
, macmodule
=HMAC
):
116 self
.__macmodule
= macmodule
117 self
.__digestmodule
= digestmodule
118 self
._setup
(passphrase
, salt
, iterations
, self
._pseudorandom
)
120 def _pseudorandom(self
, key
, msg
):
121 """Pseudorandom function. e.g. HMAC-SHA1"""
122 return self
.__macmodule
.new(key
=key
, msg
=msg
,
123 digestmod
=self
.__digestmodule
).digest()
125 def read(self
, bytes
):
126 """Read the specified number of key bytes."""
128 raise ValueError("file-like object is closed")
130 size
= len(self
.__buf
)
131 blocks
= [self
.__buf
]
135 if i
> 0xffffffffL
or i
< 1:
136 # We could return "" here, but
137 raise OverflowError("derived key too long")
141 buf
= "".join(blocks
)
143 self
.__buf
= buf
[bytes
:]
148 # i must fit within 32 bits
149 assert 1 <= i
<= 0xffffffffL
150 U
= self
.__prf
(self
.__passphrase
, self
.__salt
+ pack("!L", i
))
152 for j
in xrange(2, 1+self
.__iterations
):
153 U
= self
.__prf
(self
.__passphrase
, U
)
154 result
= strxor(result
, U
)
157 def hexread(self
, octets
):
158 """Read the specified number of octets. Return them as hexadecimal.
160 Note that len(obj.hexread(n)) == 2*n.
162 return b2a_hex(self
.read(octets
))
164 def _setup(self
, passphrase
, salt
, iterations
, prf
):
167 # passphrase and salt must be str or unicode (in the latter
168 # case, we convert to UTF-8)
169 if isinstance(passphrase
, unicode):
170 passphrase
= passphrase
.encode("UTF-8")
171 if not isinstance(passphrase
, str):
172 raise TypeError("passphrase must be str or unicode")
173 if isinstance(salt
, unicode):
174 salt
= salt
.encode("UTF-8")
175 if not isinstance(salt
, str):
176 raise TypeError("salt must be str or unicode")
178 # iterations must be an integer >= 1
179 if not isinstance(iterations
, (int, long)):
180 raise TypeError("iterations must be an integer")
182 raise ValueError("iterations must be at least 1")
184 # prf must be callable
185 if not callable(prf
):
186 raise TypeError("prf must be callable")
188 self
.__passphrase
= passphrase
190 self
.__iterations
= iterations
197 """Close the stream."""
199 del self
.__passphrase
201 del self
.__iterations
207 def crypt(word
, salt
=None, iterations
=None):
208 """PBKDF2-based unix crypt(3) replacement.
210 The number of iterations specified in the salt overrides the 'iterations'
213 The effective hash length is 192 bits.
216 # Generate a (pseudo-)random salt if the user hasn't provided one.
220 # salt must be a string or the us-ascii subset of unicode
221 if isinstance(salt
, unicode):
222 salt
= salt
.encode("us-ascii")
223 if not isinstance(salt
, str):
224 raise TypeError("salt must be a string")
226 # word must be a string or unicode (in the latter case, we convert to UTF-8)
227 if isinstance(word
, unicode):
228 word
= word
.encode("UTF-8")
229 if not isinstance(word
, str):
230 raise TypeError("word must be a string or unicode")
232 # Try to extract the real salt and iteration count from the salt
233 if salt
.startswith("$p5k2$"):
234 (iterations
, salt
, dummy
) = salt
.split("$")[2:5]
238 converted
= int(iterations
, 16)
239 if iterations
!= "%x" % converted
: # lowercase hex, minimum digits
240 raise ValueError("Invalid salt")
241 iterations
= converted
242 if not (iterations
>= 1):
243 raise ValueError("Invalid salt")
245 # Make sure the salt matches the allowed character set
246 allowed
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
248 if ch
not in allowed
:
249 raise ValueError("Illegal character %r in salt" % (ch
,))
251 if iterations
is None or iterations
== 400:
253 salt
= "$p5k2$$" + salt
255 salt
= "$p5k2$%x$%s" % (iterations
, salt
)
256 rawhash
= PBKDF2(word
, salt
, iterations
).read(24)
257 return salt
+ "$" + b64encode(rawhash
, "./")
259 # Add crypt as a static method of the PBKDF2 class
260 # This makes it easier to do "from PBKDF2 import PBKDF2" and still use
262 PBKDF2
.crypt
= staticmethod(crypt
)
265 """Return a 48-bit pseudorandom salt for crypt().
267 This function is not suitable for generating cryptographic secrets.
269 binarysalt
= "".join([pack("@H", randint(0, 0xffff)) for i
in range(3)])
270 return b64encode(binarysalt
, "./")
273 """Module self-test"""
274 from binascii
import a2b_hex
277 # Test vectors from RFC 3962
281 result
= PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1).read(16)
282 expected
= a2b_hex("cdedb5281bb2f801565a1122b2563515")
283 if result
!= expected
:
284 raise RuntimeError("self-test failed")
287 result
= PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1200).hexread(32)
288 expected
= ("5c08eb61fdf71e4e4ec3cf6ba1f5512b"
289 "a7e52ddbc5e5142f708a31e2e62b1e13")
290 if result
!= expected
:
291 raise RuntimeError("self-test failed")
294 result
= PBKDF2("X"*64, "pass phrase equals block size", 1200).hexread(32)
295 expected
= ("139c30c0966bc32ba55fdbf212530ac9"
296 "c5ec59f1a452f5cc9ad940fea0598ed1")
297 if result
!= expected
:
298 raise RuntimeError("self-test failed")
301 result
= PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32)
302 expected
= ("9ccad6d468770cd51b10e6a68721be61"
303 "1a8b4d282601db3b36be9246915ec82a")
304 if result
!= expected
:
305 raise RuntimeError("self-test failed")
312 f
= PBKDF2("kickstart", "workbench", 256)
318 expected
= PBKDF2("kickstart", "workbench", 256).read(40)
319 if result
!= expected
:
320 raise RuntimeError("self-test failed")
323 # crypt() test vectors
327 result
= crypt("cloadm", "exec")
328 expected
= '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
329 if result
!= expected
:
330 raise RuntimeError("self-test failed")
333 result
= crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
334 expected
= '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
335 if result
!= expected
:
336 raise RuntimeError("self-test failed")
339 result
= crypt("dcl", "tUsch7fU", iterations
=13)
340 expected
= "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
341 if result
!= expected
:
342 raise RuntimeError("self-test failed")
345 result
= crypt(u
'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
346 '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
347 expected
= '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'
348 if result
!= expected
:
349 raise RuntimeError("self-test failed")
351 if __name__
== '__main__':
354 # vim:set ts=4 sw=4 sts=4 expandtab: