这里是一个基于规范的纯Python3实现的sha512_crypt
函数。 这仅用于说明,始终使用crypt.crypt
代替!
import hashlib, base64
SHUFFLE_SHA512_INDICES = [
42, 21, 0, 1, 43, 22, 23, 2, 44, 45, 24, 3, 4, 46, 25,
26, 5, 47, 48, 27, 6, 7, 49, 28, 29, 8, 50, 51, 30, 9,
10, 52, 31, 32, 11, 53, 54, 33, 12, 13, 55, 34, 35, 14, 56,
57, 36, 15, 16, 58, 37, 38, 17, 59, 60, 39, 18, 19, 61, 40,
41, 20, 62, 63
]
def shuffle_sha512(data):
return bytes(data[i] for i in SHUFFLE_SHA512_INDICES)
def extend_by_repeat(data, length):
return (data * (length // len(data) + 1))[:length]
CUSTOM_ALPHABET = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
''' Base64 encode based on SECTION 22.e)
'''
def custom_b64encode(data, alphabet = CUSTOM_ALPHABET):
buffer,count,result = 0,0,[]
for byte in data:
buffer |= byte << count
count += 8
while count >= 6:
result.append(buffer & 0x3f)
buffer >>= 6
count -= 6
if count > 0:
result.append(buffer)
return ''.join(alphabet[idx] for idx in result)
''' From http://www.akkadia.org/drepper/SHA-crypt.txt
'''
def sha512_crypt(password, salt, rounds_in = None):
rounds,rounds_defined = 5000, False
if rounds_in is not None:
rounds,rounds_defined = rounds_in, True
assert 1000 <= rounds <= 999999999
hash = hashlib.sha512
salt_prefix = '$6$'
password = password.encode('utf8')
salt = salt.encode('ascii')[:16]
A = hash()
A.update(password)
A.update(salt)
B = hash()
B.update(password)
B.update(salt)
B.update(password)
digestB = B.digest();
A.update(extend_by_repeat(digestB, len(password)))
i = len(password)
while i > 0:
if i & 1:
A.update(digestB)
else:
A.update(password)
i = i >> 1
digestA = A.digest()
DP = hash()
for _ in range(len(password)):
DP.update(password)
digestDP = DP.digest()
P = extend_by_repeat(digestDP, len(password))
DS = hash()
for _ in range(16 + digestA[0]):
DS.update(salt)
digestDS = DS.digest()
S = extend_by_repeat(digestDS, len(salt))
digest_iteration_AC = digestA
for i in range(rounds):
C = hash()
if i % 2:
C.update(P)
else:
C.update(digest_iteration_AC)
if i % 3:
C.update(S)
if i % 7:
C.update(P)
if i % 2:
C.update(digest_iteration_AC)
else:
C.update(P)
digest_iteration_AC = C.digest()
shuffled_digest = shuffle_sha512(digest_iteration_AC)
prefix = salt_prefix
if rounds_defined:
prefix += 'rounds={0}$'.format(rounds_in)
return (prefix
+ salt.decode('ascii')
+ '$'
+ custom_b64encode(shuffled_digest)
)
actual = sha512_crypt('password', 'salt1234')
expected = '$6$salt1234$Zr07alHmuONZlfKILiGKKULQZaBG6Qmf5smHCNH35KnciTapZ7dItwaCv5SKZ1xH9ydG59SCgkdtsTqVWGhk81'
print(actual)
print(expected)
assert actual == expected
sha512_crypt.encrypt(password, salt=salt, rounds=5000)
。 - Padraic Cunningham