2015-12-25 1 views
4

Exécution mkpasswd -m sha-512 -S salt1234 password résultats dans les domaines suivants:SHA 512 sortie crypte écrite avec le code Python est différent de mkpasswd

$6$salt1234$Zr07alHmuONZlfKILiGKKULQZaBG6Qmf5smHCNH35KnciTapZ7dItwaCv5SKZ1xH9ydG59SCgkdtsTqVWGhk81 

J'ai ce bout de code Python que je pensais que génèrerait la même chose, mais n'est pas:

import hashlib, base64 
print(base64.b64encode(hashlib.sha512('password' + 'salt1234').digest())) 

Il en résulte plutôt dans:

nOkBUt6l7zlKAfjtk1EfB0TmckXfDiA4FPLcpywOLORZ1PWQK4+PZVEiT4+9rFjqR3xnaruZBiRjDGcDpxxTig== 

Je ne sais pas ce que je suis doi mal.

Une autre question que j'ai est, comment puis-je dire à la fonction sha512 pour faire des tours personnalisés. Il semble prendre seulement 1 argument.

+3

'mkpasswd' est un front-end à la [' 'fonction crypt()] (http://linux.die.net/man/3/crypt).Je ne pense pas que ce soit un hachage SHA512 simple ici. –

+1

Je pense que passlib pourrait être plus proche de ce que vous voulez https://pythonhosted.org/passlib/lib/passlib.hash.sha512_crypt.html# Il y a aussi une grande différence entre hashlib et crypto (3) –

+3

Spec for SHA512-crypt : http://www.akkadia.org/drepper/sha-crypt.html –

Répondre

7

mkpasswd est un front-end à la crypt() function. Je ne pense pas que ce soit un hachage SHA512 simple ici.

Un peu de recherche pointe vers le specification for SHA256-crypt and SHA512-crypt, qui montre que le hachage est appliqué 5000 fois par défaut. Vous pouvez spécifier un nombre différent de tours à l'aide du commutateur -R sur mkpasswd; -R 5000 en effet vous donne la même sortie:

$ mkpasswd -m sha-512 -S salt1234 -R 5000 password 
$6$rounds=5000$salt1234$Zr07alHmuONZlfKILiGKKULQZaBG6Qmf5smHCNH35KnciTapZ7dItwaCv5SKZ1xH9ydG59SCgkdtsTqVWGhk81 

Le nombre minimum de tours proposés par l'outil de ligne de commande est 1000:

$ mkpasswd -m sha-512 -S salt1234 -R 999 password 
$6$rounds=1000$salt1234$SVDFHbJXYrzjGi2fA1k3ws01/D9q0ZTAh1KfRF5.ehgjVBqfHUaKqfynXefJ4DxIWxkMAITYq9mmcBl938YQ// 
$ mkpasswd -m sha-512 -S salt1234 -R 1 password 
$6$rounds=1000$salt1234$SVDFHbJXYrzjGi2fA1k3ws01/D9q0ZTAh1KfRF5.ehgjVBqfHUaKqfynXefJ4DxIWxkMAITYq9mmcBl938YQ// 

L'algorithme est un peu plus impliqué, vous obligeant à créer plusieurs digestifs. Vous pourrait à la place accéder à la fonction C crypt() à travers le crypt.crypt() function, et le conduire de la même manière que la ligne de commande mkpasswd.

Cela dépend de votre plate-forme si la méthode SHA512-crypt est disponible; la version Python 3 du module crypt propose un crypt.methods list qui vous indique les méthodes prises en charge par votre plate-forme. Puisque cette utilisation utilise exactement la même bibliothèque mkpasswd, votre système d'exploitation supporte évidemment SHA512-crypt et Python y aura également accès.

Vous devez préfixer le sel avec '$6$ pour spécifier la méthode différente. Vous pouvez spécifier le nombre de tours en ajoutant une chaîne 'rounds=<N>$' entre la chaîne '$6$' et votre sel:

import crypt 
import os 
import random 
import string 

def sha512_crypt(password, salt=None, rounds=None): 
    if salt is None: 
     rand = random.SystemRandom() 
     salt = ''.join([rand.choice(string.ascii_letters + string.digits) 
         for _ in range(8)]) 

    prefix = '$6$' 
    if rounds is not None: 
     rounds = max(1000, min(999999999, rounds or 5000)) 
     prefix += 'rounds={0}$'.format(rounds) 
    return crypt.crypt(password, prefix + salt) 

Ce produit alors la même sortie que la ligne de commande mkpasswd:

>>> sha512_crypt('password', 'salt1234') 
'$6$salt1234$Zr07alHmuONZlfKILiGKKULQZaBG6Qmf5smHCNH35KnciTapZ7dItwaCv5SKZ1xH9ydG59SCgkdtsTqVWGhk81' 
>>> sha512_crypt('password', 'salt1234', rounds=1000) 
'$6$rounds=1000$salt1234$SVDFHbJXYrzjGi2fA1k3ws01/D9q0ZTAh1KfRF5.ehgjVBqfHUaKqfynXefJ4DxIWxkMAITYq9mmcBl938YQ//' 
3

Vous devez utiliser crypt.crypt:

>>> import crypt 
>>> crypt.crypt('password', '$6$' + 'salt1234') 
'$6$salt1234$Zr07alHmuONZlfKILiGKKULQZaBG6Qmf5smHCNH35KnciTapZ7dItwaCv5SKZ1xH9ydG59SCgkdtsTqVWGhk81' 
+1

Mais pourquoi le code que j'ai écrit ne fait-il pas ce que je m'attends à faire? La fonction hashlib.sha512 n'est-elle pas la même que celle de SHA-512? J'essaie de comprendre l'algorithme de la crypte SHA-512 à partir d'un point de vue de haut niveau. – user1720897

+0

@ user1720897, Lisez le commentaire de Martijn Pieters. – falsetru

1

Voici un pur Implémentation python3 de la fonction sha512_crypt basée sur la spécification. Ceci est à titre d'illustration seulement, utilisez toujours crypt.crypt à la place!

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 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' 

''' 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()    # SECTION 1. 
    A.update(password)  # SECTION 2. 
    A.update(salt)   # SECTION 3. 

    B = hash()    # SECTION 4. 
    B.update(password)  # SECTION 5. 
    B.update(salt)   # SECTION 6. 
    B.update(password)  # SECTION 7. 
    digestB = B.digest(); # SECTION 8. 

    A.update(extend_by_repeat(digestB, len(password))) # SECTION 9., 10. 

    # SECTION 11. 
    i = len(password) 
    while i > 0: 
    if i & 1: 
     A.update(digestB) # SECTION 11.a) 
    else: 
     A.update(password) # SECTION 11.b) 
    i = i >> 1 

    digestA = A.digest() # SECTION 12. 

    DP = hash()    # SECTION 13. 
    # SECTION 14. 
    for _ in range(len(password)): 
    DP.update(password) 

    digestDP = DP.digest() # SECTION 15. 

    P = extend_by_repeat(digestDP, len(password)) # SECTION 16.a), 16.b) 

    DS = hash()    # SECTION 17. 
    # SECTION 18. 
    for _ in range(16 + digestA[0]): 
    DS.update(salt) 

    digestDS = DS.digest() # SECTION 19. 

    S = extend_by_repeat(digestDS, len(salt))  # SECTION 20.a), 20.b) 

    # SECTION 21. 
    digest_iteration_AC = digestA 
    for i in range(rounds): 
    C = hash()      # SECTION 21.a) 
    if i % 2: 
     C.update(P)      # SECTION 21.b) 
    else: 
     C.update(digest_iteration_AC) # SECTION 21.c) 
    if i % 3: 
     C.update(S)      # SECTION 21.d) 
    if i % 7: 
     C.update(P)      # SECTION 21.e) 
    if i % 2: 
     C.update(digest_iteration_AC) # SECTION 21.f) 
    else: 
     C.update(P)      # SECTION 21.g) 

    digest_iteration_AC = C.digest() # SECTION 21.h) 

    shuffled_digest = shuffle_sha512(digest_iteration_AC) 


    prefix = salt_prefix # SECTION 22.a) 

    # SECTION 22.b) 
    if rounds_defined: 
    prefix += 'rounds={0}$'.format(rounds_in) 


    return (prefix 
    + salt.decode('ascii')    # SECTION 22.c) 
    + '$'        # SECTION 22.d) 
    + custom_b64encode(shuffled_digest) # SECTION 22.e) 
) 

actual = sha512_crypt('password', 'salt1234') 
expected = '$6$salt1234$Zr07alHmuONZlfKILiGKKULQZaBG6Qmf5smHCNH35KnciTapZ7dItwaCv5SKZ1xH9ydG59SCgkdtsTqVWGhk81' 

print(actual) 
print(expected) 
assert actual == expected