Hash de um arquivo em Python

98

Quero que o python leia o EOF para que eu possa obter um hash apropriado, seja sha1 ou md5. Por favor ajude. Aqui está o que tenho até agora:

import hashlib

inputFile = raw_input("Enter the name of the file:")
openedFile = open(inputFile)
readFile = openedFile.read()

md5Hash = hashlib.md5(readFile)
md5Hashed = md5Hash.hexdigest()

sha1Hash = hashlib.sha1(readFile)
sha1Hashed = sha1Hash.hexdigest()

print "File Name: %s" % inputFile
print "MD5: %r" % md5Hashed
print "SHA1: %r" % sha1Hashed
user3358300
fonte
6
e qual é o problema?
isedev
1
Eu quero ser capaz de fazer hash de um arquivo. Preciso ler até a EOF, seja qual for o tamanho do arquivo.
user3358300
3
isso é exatamente o que file.read()faz - ler o arquivo inteiro.
isedev
A documentação do read()método diz?
Ignacio Vazquez-Abrams
Você deve passar por "o que é hash?".
Sharif Mamun

Respostas:

135

TL; DR usam buffers para não usar toneladas de memória.

Chegamos ao cerne do seu problema, acredito, quando consideramos as implicações de memória de trabalhar com arquivos muito grandes . Não queremos que esse bad boy misture 2 GB de RAM por um arquivo de 2 GB, então, como pasztorpisti aponta, temos que lidar com esses arquivos maiores em pedaços!

import sys
import hashlib

# BUF_SIZE is totally arbitrary, change for your app!
BUF_SIZE = 65536  # lets read stuff in 64kb chunks!

md5 = hashlib.md5()
sha1 = hashlib.sha1()

with open(sys.argv[1], 'rb') as f:
    while True:
        data = f.read(BUF_SIZE)
        if not data:
            break
        md5.update(data)
        sha1.update(data)

print("MD5: {0}".format(md5.hexdigest()))
print("SHA1: {0}".format(sha1.hexdigest()))

O que fizemos foi atualizar nossos hashes desse bad boy em pedaços de 64kb conforme seguimos com o prático método de atualização dândi do hashlib . Dessa forma, usamos muito menos memória do que os 2 GB que seriam necessários para fazer o hash do cara de uma vez!

Você pode testar isso com:

$ mkfile 2g bigfile
$ python hashes.py bigfile
MD5: a981130cf2b7e09f4686dc273cf7187e
SHA1: 91d50642dd930e9542c39d36f0516d45f4e1af0d
$ md5 bigfile
MD5 (bigfile) = a981130cf2b7e09f4686dc273cf7187e
$ shasum bigfile
91d50642dd930e9542c39d36f0516d45f4e1af0d  bigfile

Espero que ajude!

Além disso, tudo isso é descrito na pergunta vinculada no lado direito: Obtenha hash MD5 de arquivos grandes em Python


Termo aditivo!

Em geral, ao escrever python, ajuda a adquirir o hábito de seguir o pep-8 . Por exemplo, em Python, as variáveis ​​são normalmente separadas por sublinhado e não por camelCased. Mas isso é apenas estilo e ninguém realmente se preocupa com essas coisas, exceto as pessoas que têm que ler um estilo ruim ... que pode ser você lendo este código daqui a alguns anos.

Randall Hunt
fonte
@ranman Olá, não consegui obter a parte {0} ". format (sha1.hexdigest ()). Por que a usamos em vez de apenas usar sha1.hexdigest ()?
Belial
@Belial O que não estava funcionando? Eu estava usando isso principalmente para diferenciar os dois hashes ...
Randall Hunt
@ranman Está tudo funcionando, só nunca usei e não vi na literatura. "{0}". Format () ... desconhecido para mim. :)
Belial
1
Como devo escolher BUF_SIZE?
Martin Thoma
1
Isso não gera os mesmos resultados que os shasumbinários. A outra resposta listada abaixo (aquela que usa o memoryview) é compatível com outras ferramentas de hashing.
tedivm
61

Para o cálculo correto e eficiente do valor hash de um arquivo (em Python 3):

  • Abra o arquivo no modo binário (ou seja, adicione 'b'ao modo de arquivo) para evitar problemas de codificação de caracteres e conversão de final de linha.
  • Não leia o arquivo completo na memória, pois isso é um desperdício de memória. Em vez disso, leia sequencialmente bloco por bloco e atualize o hash para cada bloco.
  • Elimine o buffer duplo, ou seja, não use IO com buffer, porque já usamos um tamanho de bloco ideal.
  • Use readinto()para evitar agitação do buffer.

Exemplo:

import hashlib

def sha256sum(filename):
    h  = hashlib.sha256()
    b  = bytearray(128*1024)
    mv = memoryview(b)
    with open(filename, 'rb', buffering=0) as f:
        for n in iter(lambda : f.readinto(mv), 0):
            h.update(mv[:n])
    return h.hexdigest()
maxschlepzig
fonte
2
Como você sabe qual é o tamanho de bloco ideal?
Mitar
1
@Mitar, um limite inferior é o máximo do bloco físico (tradicionalmente 512 bytes ou 4 KiB com discos mais novos) e o tamanho da página do sistema (4 KiB em muitos sistemas, outras opções comuns: 8 KiB e 64 KiB). Então você basicamente faz algum benchmarking e / ou olha os resultados de benchmark publicados e trabalhos relacionados (por exemplo, verificar o que o rsync / GNU cp / ... atual usa).
maxschlepzig
Teria resource.getpagesizealguma utilidade aqui, se quiséssemos tentar otimizá-lo de forma dinâmica? E sobre o quê mmap?
jpmc26
@ jpmc26, getpagesize () não é tão útil aqui - os valores comuns são 4 KiB ou 8 KiB, algo nessa faixa, ou seja, algo muito menor que 128 KiB - 128 KiB geralmente é uma boa escolha. mmap não ajuda muito em nosso caso de uso, pois lemos sequencialmente o arquivo completo da frente para trás. mmap tem vantagens quando o padrão de acesso é mais parecido com acesso aleatório, se as páginas são acessadas mais de uma vez e / ou se o mmap simplifica o gerenciamento do buffer de leitura.
maxschlepzig
3
Eu comparei a solução de (1) @Randall Hunt e (2) a sua (nesta ordem, é importante devido ao cache de arquivo) com um arquivo de cerca de 116 GB e algoritmo sha1sum. A solução 1 foi modificada para usar um buffer de 20 * 4096 (PAGE_SIZE) e definir o parâmetro de buffer para 0. Apenas o algoritmo da solução 2 foi modificado (sha256 -> sha1). Resultado: (1) 3m37.137s (2) 3m30.003s. O sha1sum nativo no modo binário: 3m31.395s
bioinfornatics
18

Eu proporia simplesmente:

def get_digest(file_path):
    h = hashlib.sha256()

    with open(file_path, 'rb') as file:
        while True:
            # Reading is buffered, so we can read smaller chunks.
            chunk = file.read(h.block_size)
            if not chunk:
                break
            h.update(chunk)

    return h.hexdigest()

Todas as outras respostas aqui parecem complicar muito. Python já está armazenando em buffer durante a leitura (de maneira ideal, ou você configura esse armazenamento em buffer se tiver mais informações sobre o armazenamento subjacente) e por isso é melhor ler em partes que a função hash considera ideal, o que a torna mais rápida ou pelo menos menos intensiva de CPU para calcule a função hash. Portanto, em vez de desabilitar o buffer e tentar emular você mesmo, você usa o buffer do Python e controla o que deveria estar controlando: o que o consumidor de seus dados acha ideal, o tamanho do bloco hash.

Mitar
fonte
Resposta perfeita, mas seria bom se você apoiasse suas declarações com o documento relacionado: Python3 - open () e Python2 - open () . Mesmo considerando a diferença entre os dois, a abordagem do Python3 é mais sofisticada. No entanto, eu realmente gostei da perspectiva centrada no consumidor!
Murmel
hash.block_sizeé documentado da mesma forma que o 'tamanho do bloco interno do algoritmo de hash'. Hashlib não acha isso ideal . Nada na documentação do pacote sugere que update()prefira hash.block_sizeentrada de tamanho. Ele não usa menos CPU se você chamá-lo assim. Sua file.read()chamada leva a muitas criações de objetos desnecessários e cópias supérfluas do buffer de arquivo para seu novo objeto de chunk bytes.
maxschlepzig
Os hash atualizam seu estado em block_sizeblocos. Se você não os fornecer nesses chunks, eles terão que armazenar em buffer e esperar que dados suficientes apareçam, ou dividir os dados em pedaços internamente. Então, você pode simplesmente lidar com isso do lado de fora e então simplificar o que acontece internamente. Acho esse ideal. Veja por exemplo: stackoverflow.com/a/51335622/252025
Mitar
O block_sizeé muito menor do que qualquer tamanho de leitura útil. Além disso, qualquer bloco útil e tamanhos de leitura são potências de dois. Assim, o tamanho da leitura é divisível pelo tamanho do bloco para todas as leituras, exceto possivelmente a última. Por exemplo, o tamanho do bloco sha256 é de 64 bytes. Isso significa que update()é capaz de processar diretamente a entrada sem nenhum buffer de até qualquer múltiplo de block_size. Assim, somente se a última leitura não for divisível pelo tamanho do bloco, ela deve armazenar até 63 bytes uma vez. Portanto, seu último comentário está incorreto e não apóia as afirmações que você fez em sua resposta.
maxschlepzig
A questão é que não é necessário otimizar o armazenamento em buffer porque ele já é feito pelo Python durante a leitura. Portanto, você apenas precisa decidir a quantidade de looping que deseja fazer ao hash sobre o buffer existente.
Mitar
5

Programei um módulo capaz de fazer hash de arquivos grandes com diferentes algoritmos.

pip3 install py_essentials

Use o módulo assim:

from py_essentials import hashing as hs
hash = hs.fileChecksum("path/to/the/file.txt", "sha256")
phyyl
fonte
3

Aqui está um Python 3, solução POSIX (não Windows!) Que usa mmappara mapear o objeto na memória.

import hashlib
import mmap

def sha256sum(filename):
    h  = hashlib.sha256()
    with open(filename, 'rb') as f:
        with mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) as mm:
            h.update(mm)
    return h.hexdigest()
Antti Haapala
fonte
-2
import hashlib
user = input("Enter ")
h = hashlib.md5(user.encode())
h2 = h.hexdigest()
with open("encrypted.txt","w") as e:
    print(h2,file=e)


with open("encrypted.txt","r") as e:
    p = e.readline().strip()
    print(p)
Ome Mishra
fonte
2
Basicamente, você está fazendo o echo $USER_INPUT | md5sum > encrypted.txt && cat encrypted.txtque não lida com hashing de arquivos, especialmente com arquivos grandes.
Murmel
hashing! = criptografando
bugmenot123