You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
91 lines
2.5 KiB
91 lines
2.5 KiB
# -*- encoding: utf-8 -*-
|
|
#
|
|
# Copyright (c) Django Software Foundation and individual contributors.
|
|
# All rights reserved.
|
|
|
|
import hmac
|
|
import struct
|
|
import base64
|
|
import hashlib
|
|
import binascii
|
|
import operator
|
|
|
|
from functools import reduce
|
|
|
|
|
|
def _bin_to_long(x):
|
|
"""
|
|
Convert a binary string into a long integer
|
|
|
|
This is a clever optimization for fast xor vector math
|
|
"""
|
|
return int(binascii.hexlify(x), 16)
|
|
|
|
|
|
def _long_to_bin(x, hex_format_string):
|
|
"""
|
|
Convert a long integer into a binary string.
|
|
hex_format_string is like "%020x" for padding 10 characters.
|
|
"""
|
|
return binascii.unhexlify((hex_format_string % x).encode('ascii'))
|
|
|
|
|
|
def _fast_hmac(key, msg, digest):
|
|
"""
|
|
A trimmed down version of Python's HMAC implementation.
|
|
|
|
This function operates on bytes.
|
|
"""
|
|
dig1, dig2 = digest(), digest()
|
|
if len(key) > dig1.block_size:
|
|
key = digest(key).digest()
|
|
key += b'\x00' * (dig1.block_size - len(key))
|
|
dig1.update(key.translate(hmac.trans_36))
|
|
dig1.update(msg)
|
|
dig2.update(key.translate(hmac.trans_5C))
|
|
dig2.update(dig1.digest())
|
|
return dig2
|
|
|
|
|
|
def _pbkdf2(password, salt, iterations, dklen=0, digest=None):
|
|
"""
|
|
Implements PBKDF2 as defined in RFC 2898, section 5.2
|
|
|
|
HMAC+SHA256 is used as the default pseudo random function.
|
|
|
|
Right now 10,000 iterations is the recommended default which takes
|
|
100ms on a 2.2Ghz Core 2 Duo. This is probably the bare minimum
|
|
for security given 1000 iterations was recommended in 2001. This
|
|
code is very well optimized for CPython and is only four times
|
|
slower than openssl's implementation.
|
|
"""
|
|
|
|
assert iterations > 0
|
|
if not digest:
|
|
digest = hashlib.sha1
|
|
password = b'' + password
|
|
salt = b'' + salt
|
|
hlen = digest().digest_size
|
|
if not dklen:
|
|
dklen = hlen
|
|
if dklen > (2 ** 32 - 1) * hlen:
|
|
raise OverflowError('dklen too big')
|
|
l = -(-dklen // hlen)
|
|
r = dklen - (l - 1) * hlen
|
|
|
|
hex_format_string = "%%0%ix" % (hlen * 2)
|
|
|
|
def F(i):
|
|
def U():
|
|
u = salt + struct.pack(b'>I', i)
|
|
for j in range(int(iterations)):
|
|
u = _fast_hmac(password, u, digest).digest()
|
|
yield _bin_to_long(u)
|
|
return _long_to_bin(reduce(operator.xor, U()), hex_format_string)
|
|
|
|
T = [F(x) for x in range(1, l + 1)]
|
|
return b''.join(T[:-1]) + T[-1][:r]
|
|
|
|
pbkdf2 = lambda text, salt, iterations, dklen: base64.b16encode(
|
|
_pbkdf2(text.encode('utf-8'), salt, iterations, dklen)).lower()
|