2009-12-08 13 views
45

Estoy tratando de calcular el valor SHA-1 de un archivo.¿Por qué el Python calcula "hashlib.sha1" diferente de "git hash-object" para un archivo?

He fabriqué este script:

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

Para un archivo específico consigo este valor hash:
8c3e109ff260f7b11087974ef7bcdbdc69a0a3b9
Pero cuando puedo calcular el valor con hash_object git, cuando me siento este valor: d339346ca154f6ed9e92205c3c5c38112e761eb7

¿Por qué difieren? ¿Estoy haciendo algo mal, o puedo simplemente ignorar la diferencia?

+2

No puede ignorar la diferencia si planea usar los hashes juntos. –

+0

Olvidé mencionar, solo usé git como referencia, no voy a usarlos juntos. – Ikke

+1

Si el archivo puede ser bastante grande, puede procesarlo un bloque a la vez para que no lo necesite todo en la RAM de una vez: http://stackoverflow.com/questions/7829499/using-hashlib-to- compute-md5-digest-of-a-file-in-python3 – rakslice

Respuesta

51

git calcula los hash de esta manera:

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

Reference

+0

Debería haberlo buscado, gracias. – Ikke

+0

No hay problema, el enlace de referencia es bastante diferente, por casualidad lo encontré por suerte. –

+13

Debe mencionarse que git hace esto para evitar ataques de extensión de longitud. – Omnifarious

31

Para referencia, aquí es una versión más concisa:

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

Pensándolo bien: aunque nunca he visto, Creo que existe la posibilidad de que f.read() devuelva menos que el archivo completo, o para un archivo de muchos gigabytes, para que f.read() se quede sin memoria. Para la edificación de todos, vamos a considerar cómo arreglar eso: Una primera solución a eso es:

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

Sin embargo, no hay garantía de que '\n' aparece en el archivo en absoluto, por lo que el hecho de que el bucle for nos dará bloques del archivo que termina en '\n' podría darnos el mismo problema que teníamos originalmente. Tristemente, no veo ninguna forma similar de Pythonic de iterar sobre bloques del archivo lo más grande posible, lo que, creo, significa que estamos atrapados con un bucle while True: ... break y con un número mágico para el tamaño de bloque:

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

Por supuesto, ¿quién puede decir que podemos almacenar cadenas de un megabyte. Probablemente podamos, pero ¿y si estamos en una pequeña computadora embebida?

Me gustaría poder pensar en una forma más limpia que garantice que no se quede sin memoria en archivos enormes y que no tenga números mágicos y que funcione tan bien como la solución original simple de Pythonic.

+0

Pensándolo bien, esto puede tener problemas si f.read() no puede devolver todo el archivo (por ejemplo, en el caso de archivos de varios gigabytes) y, por lo tanto, probablemente deba iterar en fragmentos. – Ben

Cuestiones relacionadas