动态增长/流数据的哈希算法?

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

【讨论】:

以上是关于动态增长/流数据的哈希算法?的主要内容,如果未能解决你的问题,请参考以下文章

哈希表的静态,动态,以及key/value形式

技术分享智能感知与计算研究中心NIPS 2017论文提出深度离散哈希算法,可用于图像检索

一文看懂一致性哈希算法

哈希算法的一些理论

一致性哈希算法

BLAKE 哈希函数 及 ChaCha20流密码