__init__.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #!/usr/bin/env python3.3
  2. # -*- coding: utf-8 -*-
  3. """Fast PBKDF2_HMAC() for Python 2 and Python 3
  4. Christian Heimes <christian@python.org>
  5. """
  6. from __future__ import unicode_literals
  7. from hashlib import new as _hashlib_new
  8. import sys as _sys
  9. __all__ = ('pbkdf2_hmac', 'compare_digest')
  10. if _sys.version_info[0] == 2:
  11. from binascii import hexlify as _hexlify
  12. from binascii import unhexlify as _unhexlify
  13. from struct import pack as _pack
  14. _PY3 = False
  15. _text_type = unicode
  16. _string_type = basestring
  17. _trans_5C = b''.join(chr(x ^ 0x5C) for x in xrange(256))
  18. _trans_36 = b''.join(chr(x ^ 0x36) for x in xrange(256))
  19. if _sys.version_info < (2, 7):
  20. memoryview = buffer
  21. def _loop_counter(loop, pack=_pack):
  22. return pack(b'>I', loop)
  23. # hack from django.utils.crypto
  24. def _from_bytes(value, endianess, hexlify=_hexlify, int=int):
  25. return int(hexlify(value), 16)
  26. def _to_bytes(value, length, endianess, unhexlify=_unhexlify):
  27. fmt = '%%0%ix' % (2 * length)
  28. return _unhexlify(fmt % value)
  29. else:
  30. _trans_5C = bytes((x ^ 0x5C) for x in range(256))
  31. _trans_36 = bytes((x ^ 0x36) for x in range(256))
  32. _PY3 = True
  33. _text_type = str
  34. _string_type = str
  35. _from_bytes = int.from_bytes
  36. _to_bytes = int.to_bytes
  37. def _loop_counter(loop):
  38. return loop.to_bytes(4, 'big')
  39. def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None):
  40. """Password based key derivation function 2 (PKCS #5 v2.0)
  41. This Python implementations based on the hmac module about as fast
  42. as OpenSSL's PKCS5_PBKDF2_HMAC for short passwords and much faster
  43. for long passwords.
  44. Timings in seconds for pbkdf2_hmac('sha256', b'password10', b'salt', 50000)
  45. on an Intel I7 with 2.50 GHz:
  46. len(password) Python 3.3 OpenSSL 1.0.1e OpenSSL patched
  47. ------------- ---------- -------------- ---------------
  48. 10 0.408 0.431 0.233
  49. 100 0.418 0.509 0.228
  50. 500 0.433 1.01 0.230
  51. 1000 0.435 1.61 0.228
  52. ------------- ---------- -------------- ---------------
  53. On Python 2.7 the code runs about 50% slower than on Python 3.3.
  54. """
  55. if not isinstance(hash_name, _string_type):
  56. raise TypeError(hash_name)
  57. # no unicode, memoryview and other bytes-like objects are too hard to
  58. # support on 2.6 to 3.4
  59. if not isinstance(password, (bytes, bytearray)):
  60. password = memoryview(password).tobytes()
  61. if not isinstance(salt, (bytes, bytearray)):
  62. salt = memoryview(salt).tobytes()
  63. # Fast inline HMAC implementation
  64. inner = _hashlib_new(hash_name)
  65. outer = _hashlib_new(hash_name)
  66. blocksize = getattr(inner, 'block_size', 64)
  67. if len(password) > blocksize:
  68. password = _hashlib_new(hash_name, password).digest()
  69. password = password + b'\x00' * (blocksize - len(password))
  70. inner.update(password.translate(_trans_36))
  71. outer.update(password.translate(_trans_5C))
  72. def prf(msg, inner=inner, outer=outer):
  73. # PBKDF2_HMAC uses the password as key. We can re-use the same
  74. # digest objects and and just update copies to skip initialization.
  75. icpy = inner.copy()
  76. ocpy = outer.copy()
  77. icpy.update(msg)
  78. ocpy.update(icpy.digest())
  79. return ocpy.digest()
  80. if iterations < 1:
  81. raise ValueError(iterations)
  82. if dklen is None:
  83. dklen = outer.digest_size
  84. if dklen < 1:
  85. raise ValueError(dklen)
  86. from_bytes = _from_bytes
  87. to_bytes = _to_bytes
  88. loop_counter = _loop_counter
  89. dkey = b''
  90. loop = 1
  91. while len(dkey) < dklen:
  92. prev = prf(salt + loop_counter(loop))
  93. # endianess doesn't matter here as long to / from use the same
  94. rkey = from_bytes(prev, 'big')
  95. for i in range(iterations - 1):
  96. prev = prf(prev)
  97. # rkey = rkey ^ prev
  98. rkey ^= from_bytes(prev, 'big')
  99. loop += 1
  100. dkey += to_bytes(rkey, inner.digest_size, 'big')
  101. return dkey[:dklen]
  102. py_pbkdf2_hmac = pbkdf2_hmac
  103. try:
  104. from backports.pbkdf2._pbkdf2 import pbkdf2_hmac # noqa
  105. except ImportError:
  106. pass
  107. def compare_digest(a, b):
  108. """Constant timing comparison
  109. """
  110. if isinstance(a, _text_type) and isinstance(b, _text_type):
  111. try:
  112. a = a.encode("ascii")
  113. except UnicodeEncodeError:
  114. raise TypeError(a)
  115. try:
  116. b = b.encode("ascii")
  117. except UnicodeEncodeError:
  118. raise TypeError(b)
  119. if isinstance(a, (bytes, bytearray)):
  120. left = bytes(a)
  121. else:
  122. raise TypeError(a)
  123. if isinstance(b, (bytes, bytearray)):
  124. right = bytes(b)
  125. else:
  126. raise TypeError(b)
  127. len_a = len(a)
  128. len_b = len(b)
  129. if len_a == len_b:
  130. result = 0
  131. # loop count depends on length of b
  132. if len_a != len_b:
  133. result = 1
  134. left = right
  135. if _PY3:
  136. for l, r in zip(left, right):
  137. result |= l ^ r
  138. if not _PY3:
  139. _ord = ord
  140. for l, r in zip(left, right):
  141. result |= _ord(l) ^ _ord(r)
  142. return result == 0