initial population of Hash module
[python-cryptoplus.git] / src / CryptoPlus / Hash / pypbkdf2.py
blob2ca11d312fe4501d1b6c32ee5483fd9633761a36
1 #!/usr/bin/python
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>
7 # All rights reserved.
8 #
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
32 # import os
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)
38 # ...
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"
46 # else:
47 # print "Invalid password"
49 ###########################################################################
50 # History:
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
63 # expected.
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 ###########################################################################
71 __version__ = "1.2"
73 from struct import pack
74 from binascii import b2a_hex
75 from random import randint
76 import string
78 try:
79 # Use PyCrypto (if available)
80 from Crypto.Hash import HMAC, SHA as SHA1
82 except ImportError:
83 # PyCrypto not available. Use the Python standard library.
84 import hmac as HMAC
85 import sha as SHA1
87 def strxor(a, b):
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)
94 class PBKDF2(object):
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."""
127 if self.closed:
128 raise ValueError("file-like object is closed")
130 size = len(self.__buf)
131 blocks = [self.__buf]
132 i = self.__blockNum
133 while size < bytes:
134 i += 1
135 if i > 0xffffffffL or i < 1:
136 # We could return "" here, but
137 raise OverflowError("derived key too long")
138 block = self.__f(i)
139 blocks.append(block)
140 size += len(block)
141 buf = "".join(blocks)
142 retval = buf[:bytes]
143 self.__buf = buf[bytes:]
144 self.__blockNum = i
145 return retval
147 def __f(self, i):
148 # i must fit within 32 bits
149 assert 1 <= i <= 0xffffffffL
150 U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
151 result = U
152 for j in xrange(2, 1+self.__iterations):
153 U = self.__prf(self.__passphrase, U)
154 result = strxor(result, U)
155 return result
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):
165 # Sanity checks:
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")
181 if iterations < 1:
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
189 self.__salt = salt
190 self.__iterations = iterations
191 self.__prf = prf
192 self.__blockNum = 0
193 self.__buf = ""
194 self.closed = False
196 def close(self):
197 """Close the stream."""
198 if not self.closed:
199 del self.__passphrase
200 del self.__salt
201 del self.__iterations
202 del self.__prf
203 del self.__blockNum
204 del self.__buf
205 self.closed = True
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'
211 parameter.
213 The effective hash length is 192 bits.
216 # Generate a (pseudo-)random salt if the user hasn't provided one.
217 if salt is None:
218 salt = _makesalt()
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]
235 if iterations == "":
236 iterations = 400
237 else:
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./"
247 for ch in salt:
248 if ch not in allowed:
249 raise ValueError("Illegal character %r in salt" % (ch,))
251 if iterations is None or iterations == 400:
252 iterations = 400
253 salt = "$p5k2$$" + salt
254 else:
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
261 # crypt.
262 PBKDF2.crypt = staticmethod(crypt)
264 def _makesalt():
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, "./")
272 def test_pbkdf2():
273 """Module self-test"""
274 from binascii import a2b_hex
277 # Test vectors from RFC 3962
280 # Test 1
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")
286 # Test 2
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")
293 # Test 3
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")
300 # Test 4
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")
308 # Other test vectors
311 # Chunked read
312 f = PBKDF2("kickstart", "workbench", 256)
313 result = f.read(17)
314 result += f.read(17)
315 result += f.read(1)
316 result += f.read(2)
317 result += f.read(3)
318 expected = PBKDF2("kickstart", "workbench", 256).read(40)
319 if result != expected:
320 raise RuntimeError("self-test failed")
323 # crypt() test vectors
326 # crypt 1
327 result = crypt("cloadm", "exec")
328 expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
329 if result != expected:
330 raise RuntimeError("self-test failed")
332 # crypt 2
333 result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
334 expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
335 if result != expected:
336 raise RuntimeError("self-test failed")
338 # crypt 3
339 result = crypt("dcl", "tUsch7fU", iterations=13)
340 expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
341 if result != expected:
342 raise RuntimeError("self-test failed")
344 # crypt 4 (unicode)
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__':
352 test_pbkdf2()
354 # vim:set ts=4 sw=4 sts=4 expandtab: