【问题标题】:Translate a hashing algorithm from C to Python将散列算法从 C 转换为 Python
【发布时间】:2013-09-23 19:35:03
【问题描述】:

我的客户是一名 Python 程序员,我为他创建了一个 C++ 后端,其中包括许可证生成和检查。为了提高安全性,Python 前端还将执行许可证的有效性检查。

然而,许可证生成和检查算法基于散列方法,该方法依赖于整数具有固定字节大小的事实,并且对值进行位移不会扩展整数字节数。

这是一个简化的示例代码:

unsigned int HashString(const char* str) {
    unsigned int hash = 3151;
    while (*str != 0) {
        hash = (hash << 3) + (*str << 2) * 3;
        str++;
    }
    return hash;
}

如何将其转换为 Python?直接翻译显然会产生不同的结果:

def hash_string(str):
    hash = 3151
    for c in str:
        hash = (hash << 3) + (ord(c) << 2) * 3
    return hash

例如:

hash_string("foo bar spam")  #  228667414299004
HashString("foo bar spam")   // 3355459964

编辑:PHP 也需要这样做,因为在线商店也应该能够生成有效的许可证。

【问题讨论】:

  • 一个警告:char 不明确:有符号还是无符号? See here。应该将 C 代码更改为显式;即const unsigned char* str.
  • @NiklasR:这是非常微不足道的保护。任何可以破解您的二进制 DLL 并放入他的新版本的人大概也可以破解您的 Python 源代码。 (这在某些情况下可能仍然有用,只要您知道自己从中得到了多少。)
  • @TimPeters:好点。或者只是在你访问它的地方投射*str,而不是尽可能地传播东西——注册直到你添加到(hash &lt;&lt; 3)
  • 哈希码看起来也是“编造的”——可能不太好。例如,除非字符串为空,否则哈希的最后两位始终为 0。
  • @TimPeters:另一个好点。复制保护的标准技巧是使用 HMAC-SHA1 之类的东西,并在代码中混淆密钥/盐。 (真的,除非你能很好地隐藏密钥以至于它比算法破解更难找到,否则它不一定是一个好的算法......但是使用标准技巧可以更容易地证明任何破解它的人都必须违反了 DMCA 或您的合同条款。)

标签: python c algorithm hash code-translation


【解决方案1】:

这里的问题是 C 的 unsigned int 在超过 UINT_MAX 时会自动翻转,而 Python 的 int 只会越来越大。

最简单的解决方法就是在最后更正:

return hash % (1 << 32)

对于非常大的字符串,在每次操作后进行屏蔽可能会快一点,以避免以处理缓慢的巨大int 值结束。但对于较小的字符串,这可能会更慢,因为调用 % 12 次而不是 1 次的成本很容易超过处理 48 位 int 的成本。


PHP 可能有同样的问题,也可能有不同的问题。

PHP 的默认整数类型是 C long。在 64 位 Unix 平台上,这比 unsigned int 大,因此您必须使用与 Python 相同的技巧(%&amp;,以您更有意义的方式为准。)

但在 32 位 Unix 平台或 Windows 上,它的大小与 unsigned int 相同,但已签名,这意味着您需要不同的技巧。你实际上不能直接代表4294967293(试一试,你会得到-3)。您可以使用 GMPBCMath 整数而不是默认类型(在这种情况下,它与 Python 中的基本相同),或者您可以编写自定义代码进行打印、比较等处理 @987654337 @好像是4294967293


请注意,我只是假设 int 是 32 位,而 long 是 32 或 64,因为这恰好在当今每个流行的平台上都是正确的。但 C 标准只要求int 至少为 16 位长,long 至少为 32 位且不短于int。如果您需要处理 int 可能是 16 位(或 18 位!)的非常旧的平台,或者可能是 64 位或更多位的未来平台,您必须适当地调整您的代码。

【讨论】:

    【解决方案2】:

    &amp;屏蔽散列值:

    def hash_string(str, _width=2**32-1):
        hash = 3151
        for c in str:
            hash = ((hash << 3) + (ord(c) << 2) * 3)
        return hash & _width
    

    这会手动将散列缩减回大小。您只需要限制一次结果;并不是说那些高位对最终结果有影响。

    演示:

    >>> hash_string("foo bar spam")
    3355459964
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-09-21
      • 1970-01-01
      • 2013-05-16
      • 1970-01-01
      • 1970-01-01
      • 2012-09-03
      • 2015-06-12
      相关资源
      最近更新 更多