代码之家  ›  专栏  ›  技术社区  ›  Stavros Korokithakis

可逆哈希函数?

  •  29
  • Stavros Korokithakis  · 技术社区  · 14 年前

    我需要一个可逆的散列函数(很明显,输入要比输出小得多),它以一种随机的方式将输入映射到输出。基本上,我想要一种方法将“123”这样的数字转换为“98743624839198”这样的更大的数字,但这种方法不会保留比较,因此如果x1>x2,f(x1)>f(x2)(但两者都不一定总是错的),这一定不是永远正确的。

    这种情况的用例是,我需要找到一种方法,将小数字转换为大的、随机的数字。它们实际上不需要是随机的(事实上,它们需要是确定性的,所以相同的输入总是映射到相同的输出),但是它们确实需要 随机(至少当base64编码为字符串时,因此按Z位移位将不起作用,因为相似的数字将具有相似的msb)。

    此外,简单(快速)的计算和逆转是一个优势,但不是必需的。

    我不知道我是否清楚,或者如果有这样的算法存在,但我会感谢任何和所有的帮助!

    5 回复  |  直到 11 年前
        1
  •  38
  •   Mike 'Pomax' Kamermans    6 年前

    考虑到这个问题,没有一个答案显得特别有用。我也遇到了同样的问题,需要一个简单的、可逆的散列,而不是出于安全目的,于是决定进行位重定位。它很简单,很快,而且不需要知道任何布尔数学或crypo算法或任何其他需要实际思考的东西。

    最简单的方法可能是将一半的位向左移动,另一半向右移动:

    def hash(n):
      return ((0x0000FFFF & n)<<16) + ((0xFFFF0000 & n)>>16)
    

    这是可逆的,在hash(hash(n))=n中,并且具有非序列对{n,m},n<m,其中hash(m)<hash(n)。

    为了获得一个看起来不那么顺序的实现,您可能还需要考虑从[msb,z,…,a,lsb]到[msb,lsb,z,a,…]或[lsb,msb,a,z,…]的隔行重新排序,或者您认为的任何其他重定位都为您处理的数字提供了一个适当的非顺序序列。

    (上面的函数对于32位的数字是安全的,较大的数字保证会导致冲突,并且需要更多的位掩码覆盖来防止问题。也就是说,对于任何非安全uid,32位通常都足够了)。

    也可以看看 multiplicative inverse 下面是安迪·海登的回答。

        2
  •  16
  •   caf    14 年前

    你要的是什么 加密。在其基本操作模式ECB中的分组密码,可逆地将输入块映射到相同大小的输出块上。输入和输出块可以解释为数字。

    例如,AES是128位分组密码,因此它将输入的128位数字映射到输出的128位数字。如果128位对于您的目的来说足够好,那么您可以简单地将输入的数字填充到128位,用AES转换单个块,然后将输出格式化为128位数字。

    如果128位太大,可以使用64位分组密码,如3DES、IDEA或Blowfish。

    欧洲央行的模式被认为是软弱的,但它的软弱 假设为需求的约束(即,映射是“确定性的”)。这是一个弱点,因为一旦攻击者观察到123映射到9874362483910978,从那时起,只要她看到后者,她就知道明文是123。攻击者可以执行频率分析和/或建立已知明文/密文对的字典。

        3
  •  14
  •   Community M-A    7 年前

    另一个简单的解决方案是 multiplicative inverses (see Eri Clippert's blog) :

    我们展示了你可以采取任何两个互质正整数x和m,并计算一个第三正整数y,其性质是(x*y)%m=1,因此对于任何正整数z,(x*z *y)%m=z %m。也就是说,总是存在乘性逆,它抵消了由x模M乘以的结果。

    我们取一个大数,例如4000000000和一个大的副素数,例如387420489:

    def rhash(n):
        return n * 387420489 % 4000000000
    
    >>> rhash(12)
    649045868
    

    我们首先用 modinv 结果是3513180409:

    >>> 3513180409 * 387420489 % 4000000000
    1
    

    现在,我们可以定义反比:

    def un_rhash(h):
        return h * 3513180409 % 4000000000
    
    >>> un_rhash(649045868)  # un_rhash(rhash(12))
    12
    

    注:这个答案计算速度快,适用于4000000000以下的数字,如果需要处理较大的数字,请选择足够大的数字(和另一个协素数)。


    你可能想用十六进制来做这个(来装入int):

    def rhash(n):
        return "%08x" % (n * 387420489 % 4000000000)
    
    >>> rhash(12)
    '26afa76c'
    
    def un_rhash(h):
        return int(h, 16) * 3513180409 % 4000000000
    
    >>> un_rhash('26afa76c')  # un_rhash(rhash(12))
    12
    

    如果你选择一个相对较大的副素数,那么这看起来是随机的,是非连续的,而且计算也很快。

        4
  •  3
  •   Community M-A    7 年前

    基本上,您正在寻找双向加密,并且可能使用 salt .

    你有很多选择:

    1. 三倍
    2. 爱依斯

    下面是一个例子:“ Simple insecure two-way "obfuscation" for C#

    你在看什么语言?如果是.NET,那么看看加密名称空间中的一些想法。

        5
  •  3
  •   Flipster    14 年前

    为什么不用一个很长的数字来表示异或呢?

    容易的。快。可逆。

    或者,如果这不需要非常安全,可以从基数10转换为一些较小的基数(比如基数8或基数4,具体取决于您希望数字的长度)。