pypbkdf2.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. #!/usr/bin/python
  2. # -*- coding: ascii -*-
  3. ###########################################################################
  4. # PBKDF2.py - PKCS#5 v2.0 Password-Based Key Derivation
  5. #
  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.
  14. #
  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.
  25. #
  26. # Country of origin: Canada
  27. #
  28. ###########################################################################
  29. # Sample PBKDF2 usage:
  30. # from Crypto.Cipher import AES
  31. # from PBKDF2 import PBKDF2
  32. # import os
  33. #
  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. # ...
  39. #
  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"
  48. #
  49. ###########################################################################
  50. # History:
  51. #
  52. # 2007-07-27 Dwayne C. Litzenberger <dlitz@dlitz.net>
  53. # - Initial Release (v1.0)
  54. #
  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.
  64. #
  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).
  68. #
  69. ###########################################################################
  70. __version__ = "1.2"
  71. from struct import pack
  72. from binascii import b2a_hex
  73. from random import randint
  74. import string
  75. import collections
  76. try:
  77. # Use PyCrypto (if available)
  78. from Crypto.Hash import HMAC, SHA as SHA1
  79. except ImportError:
  80. # PyCrypto not available. Use the Python standard library.
  81. import hmac as HMAC
  82. import sha as SHA1
  83. def strxor(a, b):
  84. return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
  85. def b64encode(data, chars="+/"):
  86. tt = string.maketrans("+/", chars)
  87. return data.encode('base64').replace("\n", "").translate(tt)
  88. class PBKDF2(object):
  89. """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
  90. This implementation takes a passphrase and a salt (and optionally an
  91. iteration count, a digest module, and a MAC module) and provides a
  92. file-like object from which an arbitrarily-sized key can be read.
  93. If the passphrase and/or salt are unicode objects, they are encoded as
  94. UTF-8 before they are processed.
  95. The idea behind PBKDF2 is to derive a cryptographic key from a
  96. passphrase and a salt.
  97. PBKDF2 may also be used as a strong salted password hash. The
  98. 'crypt' function is provided for that purpose.
  99. Remember: Keys generated using PBKDF2 are only as strong as the
  100. passphrases they are derived from.
  101. """
  102. def __init__(self, passphrase, salt, iterations=1000,
  103. digestmodule=SHA1, macmodule=HMAC):
  104. self.__macmodule = macmodule
  105. self.__digestmodule = digestmodule
  106. self._setup(passphrase, salt, iterations, self._pseudorandom)
  107. def _pseudorandom(self, key, msg):
  108. """Pseudorandom function. e.g. HMAC-SHA1"""
  109. return self.__macmodule.new(key=key, msg=msg,
  110. digestmod=self.__digestmodule).digest()
  111. def read(self, bytes):
  112. """Read the specified number of key bytes."""
  113. if self.closed:
  114. raise ValueError("file-like object is closed")
  115. size = len(self.__buf)
  116. blocks = [self.__buf]
  117. i = self.__blockNum
  118. while size < bytes:
  119. i += 1
  120. if i > 0xffffffff or i < 1:
  121. # We could return "" here, but
  122. raise OverflowError("derived key too long")
  123. block = self.__f(i)
  124. blocks.append(block)
  125. size += len(block)
  126. buf = "".join(blocks)
  127. retval = buf[:bytes]
  128. self.__buf = buf[bytes:]
  129. self.__blockNum = i
  130. return retval
  131. def __f(self, i):
  132. # i must fit within 32 bits
  133. assert 1 <= i <= 0xffffffff
  134. U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
  135. result = U
  136. for j in range(2, 1+self.__iterations):
  137. U = self.__prf(self.__passphrase, U)
  138. result = strxor(result, U)
  139. return result
  140. def hexread(self, octets):
  141. """Read the specified number of octets. Return them as hexadecimal.
  142. Note that len(obj.hexread(n)) == 2*n.
  143. """
  144. return b2a_hex(self.read(octets))
  145. def _setup(self, passphrase, salt, iterations, prf):
  146. # Sanity checks:
  147. # passphrase and salt must be str or unicode (in the latter
  148. # case, we convert to UTF-8)
  149. if isinstance(passphrase, str):
  150. passphrase = passphrase.encode("UTF-8")
  151. if not isinstance(passphrase, str):
  152. raise TypeError("passphrase must be str or unicode")
  153. if isinstance(salt, str):
  154. salt = salt.encode("UTF-8")
  155. if not isinstance(salt, str):
  156. raise TypeError("salt must be str or unicode")
  157. # iterations must be an integer >= 1
  158. if not isinstance(iterations, int):
  159. raise TypeError("iterations must be an integer")
  160. if iterations < 1:
  161. raise ValueError("iterations must be at least 1")
  162. # prf must be callable
  163. if not isinstance(prf, collections.Callable):
  164. raise TypeError("prf must be callable")
  165. self.__passphrase = passphrase
  166. self.__salt = salt
  167. self.__iterations = iterations
  168. self.__prf = prf
  169. self.__blockNum = 0
  170. self.__buf = ""
  171. self.closed = False
  172. def close(self):
  173. """Close the stream."""
  174. if not self.closed:
  175. del self.__passphrase
  176. del self.__salt
  177. del self.__iterations
  178. del self.__prf
  179. del self.__blockNum
  180. del self.__buf
  181. self.closed = True
  182. def crypt(word, salt=None, iterations=None):
  183. """PBKDF2-based unix crypt(3) replacement.
  184. The number of iterations specified in the salt overrides the 'iterations'
  185. parameter.
  186. The effective hash length is 192 bits.
  187. """
  188. # Generate a (pseudo-)random salt if the user hasn't provided one.
  189. if salt is None:
  190. salt = _makesalt()
  191. # salt must be a string or the us-ascii subset of unicode
  192. if isinstance(salt, str):
  193. salt = salt.encode("us-ascii")
  194. if not isinstance(salt, str):
  195. raise TypeError("salt must be a string")
  196. # word must be a string or unicode (in the latter case, we convert to UTF-8)
  197. if isinstance(word, str):
  198. word = word.encode("UTF-8")
  199. if not isinstance(word, str):
  200. raise TypeError("word must be a string or unicode")
  201. # Try to extract the real salt and iteration count from the salt
  202. if salt.startswith("$p5k2$"):
  203. (iterations, salt, dummy) = salt.split("$")[2:5]
  204. if iterations == "":
  205. iterations = 400
  206. else:
  207. converted = int(iterations, 16)
  208. if iterations != "%x" % converted: # lowercase hex, minimum digits
  209. raise ValueError("Invalid salt")
  210. iterations = converted
  211. if not (iterations >= 1):
  212. raise ValueError("Invalid salt")
  213. # Make sure the salt matches the allowed character set
  214. allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
  215. for ch in salt:
  216. if ch not in allowed:
  217. raise ValueError("Illegal character %r in salt" % (ch,))
  218. if iterations is None or iterations == 400:
  219. iterations = 400
  220. salt = "$p5k2$$" + salt
  221. else:
  222. salt = "$p5k2$%x$%s" % (iterations, salt)
  223. rawhash = PBKDF2(word, salt, iterations).read(24)
  224. return salt + "$" + b64encode(rawhash, "./")
  225. # Add crypt as a static method of the PBKDF2 class
  226. # This makes it easier to do "from PBKDF2 import PBKDF2" and still use
  227. # crypt.
  228. PBKDF2.crypt = staticmethod(crypt)
  229. def _makesalt():
  230. """Return a 48-bit pseudorandom salt for crypt().
  231. This function is not suitable for generating cryptographic secrets.
  232. """
  233. binarysalt = "".join([pack("@H", randint(0, 0xffff)) for i in range(3)])
  234. return b64encode(binarysalt, "./")
  235. def test_pbkdf2():
  236. """Module self-test"""
  237. from binascii import a2b_hex
  238. #
  239. # Test vectors from RFC 3962
  240. #
  241. # Test 1
  242. result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1).read(16)
  243. expected = a2b_hex("cdedb5281bb2f801565a1122b2563515")
  244. if result != expected:
  245. raise RuntimeError("self-test failed")
  246. # Test 2
  247. result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1200).hexread(32)
  248. expected = ("5c08eb61fdf71e4e4ec3cf6ba1f5512b"
  249. "a7e52ddbc5e5142f708a31e2e62b1e13")
  250. if result != expected:
  251. raise RuntimeError("self-test failed")
  252. # Test 3
  253. result = PBKDF2("X"*64, "pass phrase equals block size", 1200).hexread(32)
  254. expected = ("139c30c0966bc32ba55fdbf212530ac9"
  255. "c5ec59f1a452f5cc9ad940fea0598ed1")
  256. if result != expected:
  257. raise RuntimeError("self-test failed")
  258. # Test 4
  259. result = PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32)
  260. expected = ("9ccad6d468770cd51b10e6a68721be61"
  261. "1a8b4d282601db3b36be9246915ec82a")
  262. if result != expected:
  263. raise RuntimeError("self-test failed")
  264. #
  265. # Other test vectors
  266. #
  267. # Chunked read
  268. f = PBKDF2("kickstart", "workbench", 256)
  269. result = f.read(17)
  270. result += f.read(17)
  271. result += f.read(1)
  272. result += f.read(2)
  273. result += f.read(3)
  274. expected = PBKDF2("kickstart", "workbench", 256).read(40)
  275. if result != expected:
  276. raise RuntimeError("self-test failed")
  277. #
  278. # crypt() test vectors
  279. #
  280. # crypt 1
  281. result = crypt("cloadm", "exec")
  282. expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
  283. if result != expected:
  284. raise RuntimeError("self-test failed")
  285. # crypt 2
  286. result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
  287. expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
  288. if result != expected:
  289. raise RuntimeError("self-test failed")
  290. # crypt 3
  291. result = crypt("dcl", "tUsch7fU", iterations=13)
  292. expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
  293. if result != expected:
  294. raise RuntimeError("self-test failed")
  295. # crypt 4 (unicode)
  296. result = crypt('\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
  297. '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
  298. expected = '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'
  299. if result != expected:
  300. raise RuntimeError("self-test failed")
  301. if __name__ == '__main__':
  302. test_pbkdf2()
  303. # vim:set ts=4 sw=4 sts=4 expandtab: