Pregunta

Me gustaría crear una instancia hashlib, update(), entonces persistir su estado de alguna manera. Más tarde, me gustaría volver a crear el objeto utilizando estos datos de estado, y siguen update() ella. Por último, me gustaría obtener el hexdigest() de la carrera total acumulada de datos. la persistencia del estado tiene que sobrevivir a través de múltiples carreras.

Ejemplo:

import hashlib
m = hashlib.sha1()
m.update('one')
m.update('two')
# somehow, persist the state of m here

#later, possibly in another process
# recreate m from the persisted state
m.update('three')
m.update('four')
print m.hexdigest()
# at this point, m.hexdigest() should be equal to hashlib.sha1().update('onetwothreefour').hextdigest()

EDIT:

No se encontró una buena manera de hacer esto con el pitón en 2010 y terminó escribiendo una pequeña aplicación de ayuda en C para lograr esto. Sin embargo, hay algunas grandes respuestas a continuación que no estaban disponibles o conocidos para mí en ese momento.

¿Fue útil?

Solución

Puede hacerlo de esta manera el uso de ctypes, sin aplicación de ayuda en C se necesita: -

rehash.py

#! /usr/bin/env python

''' A resumable implementation of SHA-256 using ctypes with the OpenSSL crypto library

    Written by PM 2Ring 2014.11.13
'''

from ctypes import *

SHA_LBLOCK = 16
SHA256_DIGEST_LENGTH = 32

class SHA256_CTX(Structure):
    _fields_ = [
        ("h", c_long * 8),
        ("Nl", c_long),
        ("Nh", c_long),
        ("data", c_long * SHA_LBLOCK),
        ("num", c_uint),
        ("md_len", c_uint)
    ]

HashBuffType = c_ubyte * SHA256_DIGEST_LENGTH

#crypto = cdll.LoadLibrary("libcrypto.so")
crypto = cdll.LoadLibrary("libeay32.dll" if os.name == "nt" else "libssl.so")

class sha256(object):
    digest_size = SHA256_DIGEST_LENGTH

    def __init__(self, datastr=None):
        self.ctx = SHA256_CTX()
        crypto.SHA256_Init(byref(self.ctx))
        if datastr:
            self.update(datastr)

    def update(self, datastr):
        crypto.SHA256_Update(byref(self.ctx), datastr, c_int(len(datastr)))

    #Clone the current context
    def _copy_ctx(self):
        ctx = SHA256_CTX()
        pointer(ctx)[0] = self.ctx
        return ctx

    def copy(self):
        other = sha256()
        other.ctx = self._copy_ctx()
        return other

    def digest(self):
        #Preserve context in case we get called before hashing is
        # really finished, since SHA256_Final() clears the SHA256_CTX
        ctx = self._copy_ctx()
        hashbuff = HashBuffType()
        crypto.SHA256_Final(hashbuff, byref(self.ctx))
        self.ctx = ctx
        return str(bytearray(hashbuff))

    def hexdigest(self):
        return self.digest().encode('hex')

#Tests
def main():
    import cPickle
    import hashlib

    data = ("Nobody expects ", "the spammish ", "imposition!")

    print "rehash\n"

    shaA = sha256(''.join(data))
    print shaA.hexdigest()
    print repr(shaA.digest())
    print "digest size =", shaA.digest_size
    print

    shaB = sha256()
    shaB.update(data[0])
    print shaB.hexdigest()

    #Test pickling
    sha_pickle = cPickle.dumps(shaB, -1)
    print "Pickle length:", len(sha_pickle)
    shaC = cPickle.loads(sha_pickle)

    shaC.update(data[1])
    print shaC.hexdigest()

    #Test copying. Note that copy can be pickled
    shaD = shaC.copy()

    shaC.update(data[2])
    print shaC.hexdigest()


    #Verify against hashlib.sha256()
    print "\nhashlib\n"

    shaD = hashlib.sha256(''.join(data))
    print shaD.hexdigest()
    print repr(shaD.digest())
    print "digest size =", shaD.digest_size
    print

    shaE = hashlib.sha256(data[0])
    print shaE.hexdigest()

    shaE.update(data[1])
    print shaE.hexdigest()

    #Test copying. Note that hashlib copy can NOT be pickled
    shaF = shaE.copy()
    shaF.update(data[2])
    print shaF.hexdigest()


if __name__ == '__main__':
    main()

resumable_SHA-256.py

#! /usr/bin/env python

''' Resumable SHA-256 hash for large files using the OpenSSL crypto library

    The hashing process may be interrupted by Control-C (SIGINT) or SIGTERM.
    When a signal is received, hashing continues until the end of the
    current chunk, then the current file position, total file size, and
    the sha object is saved to a file. The name of this file is formed by
    appending '.hash' to the name of the file being hashed.

    Just re-run the program to resume hashing. The '.hash' file will be deleted
    once hashing is completed.

    Written by PM 2Ring 2014.11.14
'''

import cPickle as pickle
import os
import signal
import sys

import rehash

quit = False

blocksize = 1<<16   # 64kB
blocksperchunk = 1<<8

chunksize = blocksize * blocksperchunk

def handler(signum, frame):
    global quit
    print "\nGot signal %d, cleaning up." % signum
    quit = True


def do_hash(fname, filesize):
    hashname = fname + '.hash'
    if os.path.exists(hashname):
        with open(hashname, 'rb') as f:
            pos, fsize, sha = pickle.load(f)
        if fsize != filesize:
            print "Error: file size of '%s' doesn't match size recorded in '%s'" % (fname, hashname)
            print "%d != %d. Aborting" % (fsize, filesize)
            exit(1)
    else:
        pos, fsize, sha = 0, filesize, rehash.sha256()

    finished = False
    with open(fname, 'rb') as f:
        f.seek(pos)
        while not (quit or finished):
            for _ in xrange(blocksperchunk):
                block = f.read(blocksize)
                if block == '':
                    finished = True
                    break
                sha.update(block)

            pos += chunksize
            sys.stderr.write(" %6.2f%% of %d\r" % (100.0 * pos / fsize, fsize))
            if finished or quit:
                break

    if quit:
        with open(hashname, 'wb') as f:
            pickle.dump((pos, fsize, sha), f, -1)
    elif os.path.exists(hashname):
        os.remove(hashname)

    return (not quit), pos, sha.hexdigest()


def main():
    if len(sys.argv) != 2:
        print "Resumable SHA-256 hash of a file."
        print "Usage:\npython %s filename\n" % sys.argv[0]
        exit(1)

    fname = sys.argv[1]
    filesize = os.path.getsize(fname)

    signal.signal(signal.SIGINT, handler)
    signal.signal(signal.SIGTERM, handler)

    finished, pos, hexdigest = do_hash(fname, filesize)
    if finished:
        print "%s  %s" % (hexdigest, fname)
    else:
        print "sha-256 hash of '%s' incomplete" % fname
        print "%s" % hexdigest
        print "%d / %d bytes processed." % (pos, filesize)


if __name__ == '__main__':
    main()

demostración

import rehash
import pickle
sha=rehash.sha256("Hello ")
s=pickle.dumps(sha.ctx)
sha=rehash.sha256()
sha.ctx=pickle.loads(s)
sha.update("World")
print sha.hexdigest()

salida

a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e

Nota:. Me gustaría PM2Ring agradecimiento por su maravillosa código

Otros consejos

hashlib.sha1 es una envoltura alrededor de una biblioteca C por lo que no será capaz de conservar en vinagre a él.

Se tendría que poner en práctica los métodos __getstate__ y __setstate__ para Python para acceder a su estado interno

Se puede usar un pura aplicación Python de SHA1 si es rápido suficiente para sus necesidades

Yo estaba frente a este problema también, y no encontró ninguna solución existente, por lo que terminé escribiendo una biblioteca que hace algo muy similar a lo descrito Devesh Saini: https://github.com/kislyuk/rehash . Ejemplo:

import pickle, rehash
hasher = rehash.sha256(b"foo")
state = pickle.dumps(hasher)

hasher2 = pickle.loads(state)
hasher2.update(b"bar")

assert hasher2.hexdigest() == rehash.sha256(b"foobar").hexdigest()

Se puede construir fácilmente un objeto envoltorio alrededor del objeto hash que puede persistir de forma transparente los datos.

La desventaja obvia es que necesita para retener los datos de trazos en su totalidad para poder restaurar el estado - lo que dependiendo del tamaño de los datos que está tratando con, esto puede no satisfacer sus necesidades. Pero debería funcionar bien hasta algunas decenas de MB.

Unfortunattely la hashlib no expone a los algoritmos hash como clases apropiadas, se da rathers funciones de fábrica que el hash constructo objetos - lo que no podemos subclase adecuadamente los que no tienen carga símbolos reservados - una situación que preferiría evitar. Eso sólo significa que tiene que construyó su clase de contenedor desde el principio, que no es tal que una cabeza de pitón de todos modos.

aquí es un ejemplo de código que incluso podría llenar sus necesidades:

import hashlib
from cStringIO import StringIO

class PersistentSha1(object):
    def __init__(self, salt=""):
        self.__setstate__(salt)

    def update(self, data):
        self.__data.write(data)
        self.hash.update(data)

    def __getattr__(self, attr):
        return getattr(self.hash, attr)

    def __setstate__(self, salt=""):
        self.__data = StringIO()
        self.__data.write(salt)
        self.hash = hashlib.sha1(salt)

    def __getstate__(self):
        return self.data

    def _get_data(self):
        self.__data.seek(0)
        return self.__data.read()

    data = property(_get_data, __setstate__)

Se puede acceder a los "datos" miembro de sí mismo para obtener y establecer el estado de derecho, o puede utilizar las funciones de Python decapado:

>>> a = PersistentSha1()
>>> a
<__main__.PersistentSha1 object at 0xb7d10f0c>
>>> a.update("lixo")
>>> a.data
'lixo'
>>> a.hexdigest()
'6d6332a54574aeb35dcde5cf6a8774f938a65bec'
>>> import pickle
>>> b = pickle.dumps(a)
>>>
>>> c = pickle.loads(b)
>>> c.hexdigest()
'6d6332a54574aeb35dcde5cf6a8774f938a65bec'

>>> c.data
'lixo'
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top