2009-12-08 6 views
45

J'essaie de calculer la valeur SHA-1 d'un fichier.Pourquoi le fichier Python "hashlib.sha1" est-il différent de "git hash-object" pour un fichier?

Je l'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 que je reçois cette valeur de hachage:
8c3e109ff260f7b11087974ef7bcdbdc69a0a3b9
Mais quand je calcule la valeur avec hash_object git, puis-je obtenir cette valeur: d339346ca154f6ed9e92205c3c5c38112e761eb7

En quoi sont-ils différents? Est-ce que je fais quelque chose de mal, ou puis-je simplement ignorer la différence?

+2

Vous ne pouvez pas vraiment ignorer la différence si vous prévoyez d'utiliser les hachages ensemble. –

+0

Oublié de mentionner, juste utilisé git comme une référence, ne va pas les utiliser ensemble. – Ikke

+1

Si le fichier peut être assez volumineux, vous pouvez le traiter un bloc à la fois, vous n'avez donc pas besoin de tout le contenu de la RAM à la fois: http://stackoverflow.com/questions/7829499/using-hashlib-to- compute-md5-digest-d'un fichier-dans-python3 – rakslice

Répondre

51

git calcule hash comme ceci:

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

Reference

+0

J'aurais dû le chercher, merci. – Ikke

+0

Pas de problème, le lien référencé est assez différent, juste arrivé de le trouver par hasard. –

+13

Il convient de mentionner que git fait cela pour éviter les attaques de longueur. – Omnifarious

31

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() 

À la réflexion: bien que je ne l'ai jamais vu, Je pense que f.read() peut renvoyer moins que le fichier complet, ou pour un fichier de plusieurs gigaoctets, pour que f.read() manque de mémoire. Pour la gouverne de tous, nous allons examiner comment résoudre ce problème: Une première solution à cette question 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 du tout, donc le fait que la boucle for nous donnera 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 pas de manière similaire à Python d'itérer sur des blocs du fichier aussi grand que possible, ce qui, je pense, signifie que nous sommes bloqués avec une boucle while True: ... break et 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 pouvons probablement, mais que faire si nous sommes sur un ordinateur embarqué minuscule? J'aimerais pouvoir trouver un moyen plus propre de ne pas manquer de mémoire sur des fichiers énormes et qui ne possède pas de nombres magiques et qui fonctionne aussi bien que la solution Pythonic originale.

+0

À la réflexion, cela peut poser problème si f.read() ne peut pas renvoyer le fichier entier (par exemple, dans le cas de fichiers de plusieurs gigaoctets) et qu'il devrait donc probablement parcourir plusieurs segments. – Ben

Questions connexes