web-dev-qa-db-fra.com

Pourquoi le Python calculé "hashlib.sha1" est-il différent de "git hash-object" pour un fichier?

J'essaie de calculer la valeur SHA-1 d'un fichier.

J'ai fabriqué ce script:

def hashfile(filepath):
    sha1 = hashlib.sha1()
    f = open(filepath, 'rb')
    try:
        sha1.update(f.read())
    finally:
        f.close()
    return sha1.hexdigest()

Pour un fichier spécifique, j'obtiens cette valeur de hachage:
8c3e109ff260f7b11087974ef7bcdbdc69a0a3b9
Mais quand je calcule la valeur avec git hash_object, alors j'obtiens cette valeur: d339346ca154f6ed9e92205c3c5c38112e761eb7

Comment se fait-il qu'ils diffèrent? Suis-je en train de faire quelque chose de mal, ou puis-je simplement ignorer la différence?

45
Ikke

git calcule des hachages comme ceci:

sha1("blob " + filesize + "\0" + data)

Référence

51
Brian R. Bondy

Pour référence, voici une version plus concise:

def sha1OfFile(filepath):
    import hashlib
    with open(filepath, 'rb') as f:
        return hashlib.sha1(f.read()).hexdigest()

Après réflexion: bien que je ne l'ai jamais vu, je pense qu'il est possible que f.read() renvoie moins que le fichier complet, ou pour un fichier de plusieurs gigaoctets, pour que f.read () soit à court de Mémoire. Pour l'édification de tout le monde, considérons comment résoudre ce problème: Un premier correctif à cela est:

def sha1OfFile(filepath):
    import hashlib
    sha = hashlib.sha1()
    with open(filepath, 'rb') as f:
        for line in f:
            sha.update(line)
        return sha.hexdigest()

Cependant, il n'y a aucune garantie que '\n' Apparaît dans le fichier, donc le fait que la boucle for nous fournira des blocs du fichier qui se terminent par '\n' Pourrait nous donner le même problème que nous avions à l'origine. Malheureusement, je ne vois aucun moyen Pythonique similaire d'itérer sur des blocs du fichier aussi gros que possible, ce qui, je pense, signifie que nous sommes coincés avec une boucle while True: ... break Et avec un nombre magique pour la taille du bloc :

def sha1OfFile(filepath):
    import hashlib
    sha = hashlib.sha1()
    with open(filepath, 'rb') as f:
        while True:
            block = f.read(2**10) # Magic number: one-megabyte blocks.
            if not block: break
            sha.update(block)
        return sha.hexdigest()

Bien sûr, qui peut dire que nous pouvons stocker des chaînes d'un mégaoctet. Nous le pouvons probablement, mais que faire si nous sommes sur un petit ordinateur embarqué?

J'aimerais pouvoir penser à une manière plus propre qui est garantie de ne pas manquer de mémoire sur des fichiers énormes et qui n'a pas de nombres magiques et qui fonctionne aussi bien que la solution Pythonic simple d'origine.

32
Ben