pypbkdf2.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. #!/usr/bin/python
  2. # -*- coding: ascii -*-
  3. ###########################################################################
  4. # pbkdf2 - PKCS#5 v2.0 Password-Based Key Derivation
  5. #
  6. # Copyright (C) 2007-2011 Dwayne C. Litzenberger <dlitz@dlitz.net>
  7. #
  8. # Permission is hereby granted, free of charge, to any person obtaining
  9. # a copy of this software and associated documentation files (the
  10. # "Software"), to deal in the Software without restriction, including
  11. # without limitation the rights to use, copy, modify, merge, publish,
  12. # distribute, sublicense, and/or sell copies of the Software, and to
  13. # permit persons to whom the Software is furnished to do so, subject to
  14. # the following conditions:
  15. #
  16. # The above copyright notice and this permission notice shall be
  17. # included in all copies or substantial portions of the Software.
  18. #
  19. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  22. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  23. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  24. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  25. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  26. #
  27. # Country of origin: Canada
  28. #
  29. ###########################################################################
  30. # Sample PBKDF2 usage:
  31. # from Crypto.Cipher import AES
  32. # from pbkdf2 import PBKDF2
  33. # import os
  34. #
  35. # salt = os.urandom(8) # 64-bit salt
  36. # key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
  37. # iv = os.urandom(16) # 128-bit IV
  38. # cipher = AES.new(key, AES.MODE_CBC, iv)
  39. # ...
  40. #
  41. # Sample crypt() usage:
  42. # from pbkdf2 import crypt
  43. # pwhash = crypt("secret")
  44. # alleged_pw = raw_input("Enter password: ")
  45. # if pwhash == crypt(alleged_pw, pwhash):
  46. # print "Password good"
  47. # else:
  48. # print "Invalid password"
  49. #
  50. ###########################################################################
  51. __version__ = "1.3"
  52. __all__ = ['PBKDF2', 'crypt']
  53. from struct import pack
  54. from random import randint
  55. import string
  56. import sys
  57. try:
  58. # Use PyCrypto (if available).
  59. from Crypto.Hash import HMAC, SHA as SHA1
  60. except ImportError:
  61. # PyCrypto not available. Use the Python standard library.
  62. import hmac as HMAC
  63. try:
  64. from hashlib import sha1 as SHA1
  65. except ImportError:
  66. # hashlib not available. Use the old sha module.
  67. import sha as SHA1
  68. #
  69. # Python 2.1 thru 3.2 compatibility
  70. #
  71. if sys.version_info[0] == 2:
  72. _0xffffffffL = long(1) << 32
  73. def isunicode(s):
  74. return isinstance(s, unicode)
  75. def isbytes(s):
  76. return isinstance(s, str)
  77. def isinteger(n):
  78. return isinstance(n, (int, long))
  79. def b(s):
  80. return s
  81. def binxor(a, b):
  82. return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
  83. def b64encode(data, chars="+/"):
  84. tt = string.maketrans("+/", chars)
  85. return data.encode('base64').replace("\n", "").translate(tt)
  86. from binascii import b2a_hex
  87. else:
  88. _0xffffffffL = 0xffffffff
  89. def isunicode(s):
  90. return isinstance(s, str)
  91. def isbytes(s):
  92. return isinstance(s, bytes)
  93. def isinteger(n):
  94. return isinstance(n, int)
  95. def callable(obj):
  96. return hasattr(obj, '__call__')
  97. def b(s):
  98. return s.encode("latin-1")
  99. def binxor(a, b):
  100. return bytes([x ^ y for (x, y) in zip(a, b)])
  101. from base64 import b64encode as _b64encode
  102. def b64encode(data, chars="+/"):
  103. if isunicode(chars):
  104. return _b64encode(data, chars.encode('utf-8')).decode('utf-8')
  105. else:
  106. return _b64encode(data, chars)
  107. from binascii import b2a_hex as _b2a_hex
  108. def b2a_hex(s):
  109. return _b2a_hex(s).decode('us-ascii')
  110. xrange = range
  111. class PBKDF2(object):
  112. """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
  113. This implementation takes a passphrase and a salt (and optionally an
  114. iteration count, a digest module, and a MAC module) and provides a
  115. file-like object from which an arbitrarily-sized key can be read.
  116. If the passphrase and/or salt are unicode objects, they are encoded as
  117. UTF-8 before they are processed.
  118. The idea behind PBKDF2 is to derive a cryptographic key from a
  119. passphrase and a salt.
  120. PBKDF2 may also be used as a strong salted password hash. The
  121. 'crypt' function is provided for that purpose.
  122. Remember: Keys generated using PBKDF2 are only as strong as the
  123. passphrases they are derived from.
  124. """
  125. def __init__(self, passphrase, salt, iterations=1000,
  126. digestmodule=SHA1, macmodule=HMAC):
  127. self.__macmodule = macmodule
  128. self.__digestmodule = digestmodule
  129. self._setup(passphrase, salt, iterations, self._pseudorandom)
  130. def _pseudorandom(self, key, msg):
  131. """Pseudorandom function. e.g. HMAC-SHA1"""
  132. return self.__macmodule.new(key=key, msg=msg,
  133. digestmod=self.__digestmodule).digest()
  134. def read(self, bytes):
  135. """Read the specified number of key bytes."""
  136. if self.closed:
  137. raise ValueError("file-like object is closed")
  138. size = len(self.__buf)
  139. blocks = [self.__buf]
  140. i = self.__blockNum
  141. while size < bytes:
  142. i += 1
  143. if i > _0xffffffffL or i < 1:
  144. # We could return "" here, but
  145. raise OverflowError("derived key too long")
  146. block = self.__f(i)
  147. blocks.append(block)
  148. size += len(block)
  149. buf = b("").join(blocks)
  150. retval = buf[:bytes]
  151. self.__buf = buf[bytes:]
  152. self.__blockNum = i
  153. return retval
  154. def __f(self, i):
  155. # i must fit within 32 bits
  156. assert 1 <= i <= _0xffffffffL
  157. U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
  158. result = U
  159. for j in xrange(2, 1+self.__iterations):
  160. U = self.__prf(self.__passphrase, U)
  161. result = binxor(result, U)
  162. return result
  163. def hexread(self, octets):
  164. """Read the specified number of octets. Return them as hexadecimal.
  165. Note that len(obj.hexread(n)) == 2*n.
  166. """
  167. return b2a_hex(self.read(octets))
  168. def _setup(self, passphrase, salt, iterations, prf):
  169. # Sanity checks:
  170. # passphrase and salt must be str or unicode (in the latter
  171. # case, we convert to UTF-8)
  172. if isunicode(passphrase):
  173. passphrase = passphrase.encode("UTF-8")
  174. elif not isbytes(passphrase):
  175. raise TypeError("passphrase must be str or unicode")
  176. if isunicode(salt):
  177. salt = salt.encode("UTF-8")
  178. elif not isbytes(salt):
  179. raise TypeError("salt must be str or unicode")
  180. # iterations must be an integer >= 1
  181. if not isinteger(iterations):
  182. raise TypeError("iterations must be an integer")
  183. if iterations < 1:
  184. raise ValueError("iterations must be at least 1")
  185. # prf must be callable
  186. if not callable(prf):
  187. 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 = b("")
  194. self.closed = False
  195. def close(self):
  196. """Close the stream."""
  197. if not self.closed:
  198. del self.__passphrase
  199. del self.__salt
  200. del self.__iterations
  201. del self.__prf
  202. del self.__blockNum
  203. del self.__buf
  204. self.closed = True
  205. def crypt(word, salt=None, iterations=None):
  206. """PBKDF2-based unix crypt(3) replacement.
  207. The number of iterations specified in the salt overrides the 'iterations'
  208. parameter.
  209. The effective hash length is 192 bits.
  210. """
  211. # Generate a (pseudo-)random salt if the user hasn't provided one.
  212. if salt is None:
  213. salt = _makesalt()
  214. # salt must be a string or the us-ascii subset of unicode
  215. if isunicode(salt):
  216. salt = salt.encode('us-ascii').decode('us-ascii')
  217. elif isbytes(salt):
  218. salt = salt.decode('us-ascii')
  219. else:
  220. raise TypeError("salt must be a string")
  221. # word must be a string or unicode (in the latter case, we convert to UTF-8)
  222. if isunicode(word):
  223. word = word.encode("UTF-8")
  224. elif not isbytes(word):
  225. raise TypeError("word must be a string or unicode")
  226. # Try to extract the real salt and iteration count from the salt
  227. if salt.startswith("$p5k2$"):
  228. (iterations, salt, dummy) = salt.split("$")[2:5]
  229. if iterations == "":
  230. iterations = 400
  231. else:
  232. converted = int(iterations, 16)
  233. if iterations != "%x" % converted: # lowercase hex, minimum digits
  234. raise ValueError("Invalid salt")
  235. iterations = converted
  236. if not (iterations >= 1):
  237. raise ValueError("Invalid salt")
  238. # Make sure the salt matches the allowed character set
  239. allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
  240. for ch in salt:
  241. if ch not in allowed:
  242. raise ValueError("Illegal character %r in salt" % (ch,))
  243. if iterations is None or iterations == 400:
  244. iterations = 400
  245. salt = "$p5k2$$" + salt
  246. else:
  247. salt = "$p5k2$%x$%s" % (iterations, salt)
  248. rawhash = PBKDF2(word, salt, iterations).read(24)
  249. return salt + "$" + b64encode(rawhash, "./")
  250. # Add crypt as a static method of the PBKDF2 class
  251. # This makes it easier to do "from PBKDF2 import PBKDF2" and still use
  252. # crypt.
  253. PBKDF2.crypt = staticmethod(crypt)
  254. def _makesalt():
  255. """Return a 48-bit pseudorandom salt for crypt().
  256. This function is not suitable for generating cryptographic secrets.
  257. """
  258. binarysalt = b("").join([pack("@H", randint(0, 0xffff)) for i in range(3)])
  259. return b64encode(binarysalt, "./")
  260. if __name__ == '__main__':
  261. import unittest
  262. class TestPBKDF2(unittest.TestCase):
  263. def test_pbkdf2(self):
  264. """Module self-test"""
  265. from binascii import a2b_hex as _a2b_hex
  266. def a2b_hex(s):
  267. return _a2b_hex(b(s))
  268. #
  269. # Test vectors from RFC 3962
  270. #
  271. # Test 1
  272. result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1).read(16)
  273. expected = a2b_hex("cdedb5281bb2f801565a1122b2563515")
  274. self.assertEqual(expected, result)
  275. # Test 2
  276. result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1200).hexread(32)
  277. expected = ("5c08eb61fdf71e4e4ec3cf6ba1f5512b"
  278. "a7e52ddbc5e5142f708a31e2e62b1e13")
  279. self.assertEqual(expected, result)
  280. # Test 3
  281. result = PBKDF2("X"*64, "pass phrase equals block size", 1200).hexread(32)
  282. expected = ("139c30c0966bc32ba55fdbf212530ac9"
  283. "c5ec59f1a452f5cc9ad940fea0598ed1")
  284. self.assertEqual(expected, result)
  285. # Test 4
  286. result = PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32)
  287. expected = ("9ccad6d468770cd51b10e6a68721be61"
  288. "1a8b4d282601db3b36be9246915ec82a")
  289. self.assertEqual(expected, result)
  290. #
  291. # Other test vectors
  292. #
  293. # Chunked read
  294. f = PBKDF2("kickstart", "workbench", 256)
  295. result = f.read(17)
  296. result += f.read(17)
  297. result += f.read(1)
  298. result += f.read(2)
  299. result += f.read(3)
  300. expected = PBKDF2("kickstart", "workbench", 256).read(40)
  301. self.assertEqual(expected, result)
  302. #
  303. # crypt() test vectors
  304. #
  305. # crypt 1
  306. result = crypt("cloadm", "exec")
  307. expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
  308. self.assertEqual(expected, result)
  309. # crypt 2
  310. result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
  311. expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
  312. self.assertEqual(expected, result)
  313. # crypt 3
  314. result = crypt("dcl", "tUsch7fU", iterations=13)
  315. expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
  316. self.assertEqual(expected, result)
  317. # crypt 4 (unicode)
  318. result = crypt(b('\xce\x99\xcf\x89\xce\xb1\xce\xbd\xce\xbd\xce\xb7\xcf\x82').decode('utf-8'),
  319. '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
  320. expected = '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'
  321. self.assertEqual(expected, result)
  322. # crypt 5 (UTF-8 bytes)
  323. result = crypt(b('\xce\x99\xcf\x89\xce\xb1\xce\xbd\xce\xbd\xce\xb7\xcf\x82'),
  324. '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
  325. expected = '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'
  326. self.assertEqual(expected, result)
  327. def test_crypt(self):
  328. result = crypt("secret")
  329. self.assertEqual(result[:6], "$p5k2$")
  330. result = crypt("secret", "XXXXXXXX")
  331. expected = '$p5k2$$XXXXXXXX$L9mVVdq7upotdvtGvXTDTez3FIu3z0uG'
  332. self.assertEqual(expected, result)
  333. # 400 iterations (the default for crypt)
  334. result = crypt("secret", "XXXXXXXX", 400)
  335. expected = '$p5k2$$XXXXXXXX$L9mVVdq7upotdvtGvXTDTez3FIu3z0uG'
  336. self.assertEqual(expected, result)
  337. # 400 iterations (keyword argument)
  338. result = crypt("spam", "FRsH3HJB", iterations=400)
  339. expected = '$p5k2$$FRsH3HJB$SgRWDNmB2LukCy0OTal6LYLHZVgtOi7s'
  340. self.assertEqual(expected, result)
  341. # 1000 iterations
  342. result = crypt("spam", "H0NX9mT/", iterations=1000)
  343. expected = '$p5k2$3e8$H0NX9mT/$wk/sE8vv6OMKuMaqazCJYDSUhWY9YB2J'
  344. self.assertEqual(expected, result)
  345. # 1000 iterations (iterations count taken from salt parameter)
  346. expected = '$p5k2$3e8$H0NX9mT/$wk/sE8vv6OMKuMaqazCJYDSUhWY9YB2J'
  347. result = crypt("spam", expected)
  348. self.assertEqual(expected, result)
  349. unittest.main(verbosity=2)
  350. # vim:set ts=4 sw=4 sts=4 expandtab: