动态增长/流数据的哈希算法?
有没有什么算法可以让你从一个已知的哈希值继续进行哈希计算?比如说,客户端先把一部分文件上传到ServerA,我可以得到这个上传内容的md5
值。然后,客户端再把剩下的文件部分上传到ServerB,这时候我能不能把md5
的状态转移到ServerB,然后继续进行哈希计算呢?
我几年前在comp.lang.python上发现过一个基于md5的很酷的黑科技,但它是用ctypes
来处理特定版本的md5.so
或_md5.dll
,所以这个代码在不同的Python解释器版本或者其他编程语言中并不太适用。而且,md5
模块从Python 2.5开始就被弃用了,所以我需要找一个更通用的解决方案。
更重要的是,哈希的状态能不能存储在十六进制的哈希值里面?(这样我就可以用已有的哈希值继续对一串数据进行哈希,而不是用一些不太干净的内部黑科技。)
3 个回答
我也遇到了这个问题,找不到现成的解决办法,于是我写了一个库,利用ctypes来拆解OpenSSL的数据结构,这个结构保存了哈希器的状态。你可以在这里找到它:https://github.com/kislyuk/rehash。下面是一个例子:
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()
从理论上讲,这是可能的(md5 到目前为止 应该包含你继续所需的所有 状态),但看起来普通的API并没有提供你需要的东西。如果你可以接受使用CRC的话,这可能会简单得多,因为CRC在你需要的“流式”情况下更常用。你可以看看这里:
crc32()
函数接受一个可选的 crc
输入,这个输入是你要继续的校验和。
希望这能帮到你。
不是从已知的摘要开始,而是从已知的状态开始。你可以使用一个纯Python的MD5实现,并保存它的状态。这里有一个使用PyPy的_md5.py的例子:
import _md5
def md5_getstate(md):
return (md.A, md.B, md.C, md.D, md.count + [], md.input + [], md.length)
def md5_continue(state):
md = _md5.new()
(md.A, md.B, md.C, md.D, md.count, md.input, md.length) = state
return md
m1 = _md5.new()
m1.update("hello, ")
state = md5_getstate(m1)
m2 = md5_continue(state)
m2.update("world!")
print m2.hexdigest()
m = _md5.new()
m.update("hello, world!")
print m.hexdigest()
正如e.dan提到的,你也可以使用几乎任何校验和算法(比如CRC、Adler、Fletcher),但它们并不能很好地保护你免受故意的数据修改,只能防止随机错误。
补充一下:当然,你也可以使用你提到的线程中的ctypes重新实现序列化方法,以更便携的方式(不使用魔法常量)。我相信这应该是与版本和架构无关的(在Python 2.4-2.7上测试过,包括i386和x86_64):
# based on idea from http://groups.google.com/group/comp.lang.python/msg/b1c5bb87a3ff5e34
try:
import _md5 as md5
except ImportError:
# python 2.4
import md5
import ctypes
def md5_getstate(md):
if type(md) is not md5.MD5Type:
raise TypeError, 'not an MD5Type instance'
return ctypes.string_at(id(md) + object.__basicsize__,
md5.MD5Type.__basicsize__ - object.__basicsize__)
def md5_continue(state):
md = md5.new()
assert len(state) == md5.MD5Type.__basicsize__ - object.__basicsize__, \
'invalid state'
ctypes.memmove(id(md) + object.__basicsize__,
ctypes.c_char_p(state),
len(state))
return md
m1 = md5.new()
m1.update("hello, ")
state = md5_getstate(m1)
m2 = md5_continue(state)
m2.update("world!")
print m2.hexdigest()
m = md5.new()
m.update("hello, world!")
print m.hexdigest()
这不兼容Python 3,因为它没有_md5/md5模块。
不幸的是,hashlib的openssl_md5实现不适合这种黑科技,因为OpenSSL的EVP API没有提供任何可靠序列化EVP_MD_CTX对象的调用或方法。