动态增长/流数据的哈希算法?
Posted
技术标签:
【中文标题】动态增长/流数据的哈希算法?【英文标题】:Hash algorithm for dynamic growing/streaming data? 【发布时间】:2011-08-17 11:21:50 【问题描述】:是否有任何算法可以让您继续从已知的哈希摘要进行哈希处理?比如客户端上传一个文件块到ServerA,我可以得到一个md5
上传内容的总和,然后客户端把剩下的文件块上传到ServerB,我可以把md5
internals的状态转移到ServerB 并完成散列?
我多年前在 comp.lang.python 发现了一个基于 md5 的 cool black magic hack,但它使用 ctypes
来表示 md5.so
或 _md5.dll
的特定版本,因此对于不同的应用程序来说,它不是很便携的代码python解释器版本或其他编程语言。此外,md5
模块自 2.5 起在 python 标准库中已弃用,因此我需要找到更通用的解决方案。
更何况,散列的状态可以存储在十六进制摘要本身中吗? (所以我可以继续使用现有的哈希摘要对数据流进行哈希处理,而不是肮脏的内部破解。)
【问题讨论】:
【参考方案1】:不是来自已知摘要,而是来自已知状态。您可以使用纯 python MD5 实现并保存其状态。这是一个使用_md5.py from from PyPy的例子:
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 对象。
【讨论】:
Pypy 的纯 python MD5 实现很酷。但是标准 CPython 附带的 openssl_md5 怎么样? @est,你不能对 openssl_md5 可靠地执行此操作,因为 openssl 本身不提供用于 EVP_MD_CTX 序列化的 API,因此任何实现都将依赖于 openssl 版本。但是您仍然可以从 CPython 对 _md5 模块进行破解。我会将其添加到我的答案中。 真是个了不起的人!但是为什么不能用cyptes
调用libopenssl.so
直接得到EVP_MD_CTX
呢?
libssl.so 无济于事,因为正如我所说,它不提供用于 EVP_MD_CTX 序列化的 API,仅用于内存复制。因此,您不能使用记录在案的 API 将 EVP_MD_CTX “存储”到某个字节数组中,以在其他地方“恢复”它,您只能在正在运行的进程中复制它。对于复制,您可以使用 Python hashlib 的 API 中提供的 copy() 方法。当然,您可以破解 libssl.so 并手动序列化 EVP_MD_CTX,但这将取决于 openssl 版本。
谢谢。看起来确实需要一些技巧***.com/questions/5880456/…【参考方案2】:
这在理论上是可能的(md5 到目前为止 应该包含您需要继续的所有状态)但是看起来普通的 API 不能提供您需要的东西.如果您可以使用 CRC 代替,这可能会容易得多,因为这些更常用于您需要的“流”案例。见这里:
binascii.crc32(data[, crc])
crc32()
接受可选的crc
输入,这是要继续的校验和。
希望对您有所帮助。
【讨论】:
【参考方案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()
【讨论】:
以上是关于动态增长/流数据的哈希算法?的主要内容,如果未能解决你的问题,请参考以下文章