【问题标题】: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 标准库中已弃用,因此我需要找到更通用的解决方案。

更何况,散列的状态可以存储在十六进制摘要本身中吗? (所以我可以继续使用现有的哈希摘要对数据流进行哈希处理,而不是肮脏的内部破解。)

【问题讨论】:

    标签: python md5 hash data-stream


    【解决方案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 版本。
    • 谢谢。看起来确实需要一些技巧stackoverflow.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()
      

      【讨论】:

        猜你喜欢
        • 2011-01-14
        • 1970-01-01
        • 2016-06-23
        • 2011-04-06
        • 2019-07-17
        • 1970-01-01
        • 1970-01-01
        • 2012-06-03
        相关资源
        最近更新 更多