【问题标题】:Symmetric Bijective Algorithm for Integers整数的对称双射算法
【发布时间】:2011-03-09 01:18:12
【问题描述】:

我需要一种算法,可以将一个 32 位有符号整数一对一映射(即无冲突)到另一个 32 位有符号整数。

我真正关心的是足够的熵,以至于函数的输出看起来是随机的。基本上我正在寻找一种类似于 XOR Cipher 的密码,但它可以生成更多看起来任意的输出。安全不是我真正关心的问题,尽管默默无闻是。

为澄清目的而编辑:

  1. 算法必须是对称的,这样我就可以在没有密钥对的情况下反转操作。
  2. 算法必须是双射的,每个 32 位输入数字必须生成一个 32 位唯一数字。
  3. 函数的输出必须足够晦涩,只在输入中添加一个会对输出产生很大的影响。

示例预期结果:

F(100) = 98456
F(101) = -758
F(102) = 10875498
F(103) = 986541
F(104) = 945451245
F(105) = -488554

就像 MD5 一样,改变一件事可能会改变很多事情。

我正在寻找一个数学函数,所以手动映射整数不是我的解决方案。对于那些询问的人来说,算法速度并不是很重要。

【问题讨论】:

  • 您希望算法运行多快?
  • @Scorpi0 编码无所谓,解码应该更快,一般接近公钥密码的速度是可以接受的。
  • @eyazici:如果算法是对称的,编码和解码是一样的。现在你说解码应该更快。你真的需要对称算法吗?
  • @Scorpi0:算法必须是对称的,这样我才能扭转这个过程。我以 PKI 速度作为衡量标准。
  • @eyaici:如果编码函数是双射的,则该过程是可逆的。但是编码和解码功能必须是相同的功能吗?这就是我对“对称”一词的理解。如果您通过“对称算法”表示其他意思,请澄清。

标签: algorithm encryption-symmetric block-cipher bijection


【解决方案1】:

使用任何 32 位分组密码!根据定义,分组密码以可逆的方式将其范围内的每个可能的输入值映射到唯一的输出值,并且根据设计,如果没有密钥,很难确定任何给定值将映射到什么。只需选择一个密钥,如果安全性或隐蔽性很重要,请对其保密,然后将密码用作您的转换。

要将此想法扩展到非 2 次幂范围,请参阅我在 Secure Permutations with Block Ciphers 上的帖子。

解决您的具体问题:

  1. 算法确实是对称的。我不确定您所说的“在没有密钥对的情况下反转操作”是什么意思。如果您不想使用密钥,请对随机生成的密钥进行硬编码,并将其视为算法的一部分。
  2. 是的 - 根据定义,分组密码是双射的。
  3. 是的。如果不是这样,它就不是一个好的密码。

【讨论】:

  • 是的,算法是对称的,我的意思是我不想要使用非对称(如果有的话)的解决方案,非对称密码使用不同的密钥进行编码和解码,它们一起被称为“密钥对”。在您的建议的帮助下,我找到了一个 32 位分组密码(可能是网络上唯一的一个),我现在正在为它移植另一种语言,它似乎已经足够好了:qualcomm.com.au/PublicationsDocs/skip32.c
  • 啊,明白了。正如我的帖子所指出的,可以缩短现有密码; TEA 可以修改为 32 位块长度。我不会依赖这样的修改来保证加密安全,但你似乎并不担心这一点。 :)
  • 当然,32 位密钥的彩虹表大小只有 16 GB,并且可以在第一次运行时生成,因此这在现代硬件上可能永远不会是加密安全的。
  • 一些可能的密码不分先后:Hasty Pudding 密码、GDES、Simon、Speck、RC5。可能还有更多。
【解决方案2】:

我将尝试通过一个更简单的示例来解释我的解决方案,然后可以轻松地将其扩展到您的大型示例。

假设我有一个 4 位数字。有 16 个不同的值。把它看成是一个四维立方体:
(来源:ams.org
.

每个顶点代表其中一个数字,每个位代表一个维度。所以它基本上是 XYZW,其中每个维度只能有 0 或 1 值。现在假设您使用不同顺序的维度。例如 XZYW。现在每个顶点都改变了它的数量!

您可以对任意数量的维度执行此操作,只需置换这些维度。如果您不关心安全性,这对您来说可能是一个不错的快速解决方案。另一方面,我不知道输出是否足够“模糊”以满足您的需求,当然在完成大量映射之后,映射可以反转(这可能是优点或缺点,取决于您的需要。)

【讨论】:

  • 这不等同于简单地将数字的各个位切换到给定模式吗?无论如何,超立方体很酷。
  • 是的。添加超立方体示例只是为了解释,因为我认为真正理解在多维空间中交换位的“作用”会很好。
  • 我明白了。我以前从未真正见过多维被用作 32 位整数的类比,但这确实很有趣。
  • 在 n 位值中交换位只会得到 n!可能性。你想要的是一个真正的排列,它给出 (2^n)!可能性。
【解决方案3】:

以下论文为您提供 4 或 5 个映射示例,为您提供函数而不是构建映射集:www.cs.auckland.ac.nz/~john-rugis/pdf/BijectiveMapping.pdf

【讨论】:

    【解决方案4】:

    如果您的目标只是获得一个大致定义大小的数字的看似随机排列,那么还有另一种可能的方法:将数字集减少为素数。

    那么就可以使用表单的映射了

    f(i) = (i * a + b) % p

    如果 p 确实是素数,这将是所有 a != 0 和所有 b 的双射。对于较大的 a 和 b,它看起来相当随机。

    例如,在我偶然发现这个问题的情况下,我使用 1073741789 作为小于 1

    然后我的编码是

    ((n + 173741789) * 507371178) % 1073741789
    

    解码是

    (n * 233233408 + 1073741789 - 173741789) % 1073741789
    

    请注意,507371178 * 233233408 % 1073741789 == 1,因此这两个数字与模 1073741789 的数字域相反(您可以使用扩展欧几里得算法计算出此类域中的反数)。

    我相当随意地选择了 a 和 b,我只是确保它们大约是 p 的一半。

    【讨论】:

      【解决方案5】:

      除了生成随机查找表之外,您还可以使用函数组合:

      • 异或
      • 对称位置换(例如移位 16 位,或将 0-31 翻转为 31-0,或将 0-3 翻转为 3-0,将 4-7 翻转为 7-4,...)
      • 更多?

      【讨论】:

      • 正如我在问题中所写的,我需要外观随意的输出。我已经提到了 XOR-way,不是吗?
      • 当然可以,但我认为将 XOR 与位置换结合起来看起来比较随意。不管这对你来说意味着什么。
      • 是的,尝试位排列可以解决我的问题,但我正在寻找一种经过验证的尝试,而不是自己实现它,这就是我在这里问的原因。
      • 啊。在这种情况下,我会研究 Nick Johnson 提到的分组密码。有点难以理解,但尝试过并且是真实的。
      【解决方案6】:

      您可以使用随机生成的查找表吗?只要表中的随机数是唯一的,就会得到一个双射映射。不过,它不是对称的。

      为所有 32 位值创建一个 16 GB 查找表可能不实用,但您可以为高位字和低位字使用两个单独的 16 位查找表。

      PS:我认为您可以生成一个对称双射查找表,如果这很重要的话。该算法将从一个空 LUT 开始:

      +----+        +----+
      |  1 |   ->   |    |
      +----+        +----+
      |  2 |   ->   |    |
      +----+        +----+
      |  3 |   ->   |    |
      +----+        +----+
      |  4 |   ->   |    |
      +----+        +----+
      

      选择第一个元素,为其分配一个随机映射。为了使映射对称,也分配逆:

      +----+        +----+
      |  1 |   ->   |  3 |
      +----+        +----+
      |  2 |   ->   |    |
      +----+        +----+
      |  3 |   ->   |  1 |
      +----+        +----+
      |  4 |   ->   |    |
      +----+        +----+
      

      选择下一个数字,再次分配一个随机映射,但选择一个尚未分配的数字。 (即在这种情况下,不要选择 1 或 3)。重复直到 LUT 完成。这应该会生成一个随机双射对称映射。

      【讨论】:

      • 使用查找表是一个确定的解决方案,但是我需要一个数学解决方案。
      • 您所说的“数学解决方案”是什么意思?使用具有固定种子(例如密钥)的 PRNG,您就有了数学解决方案。
      • 我的意思是一个更聪明的解决方案,映射 2^32 个整数既不经济也不可扩展。基本上我一直在寻找 32 位分组密码(这在 32 位的情况下很少见)并且我找到了。
      【解决方案7】:

      取一个数,乘以 9,反数,除以 9。

      123  <> 1107 <> 7011 <> 779
      256  <> 2304 <> 4032 <> 448
      1028 <> 9252 <> 2529 <> 281
      

      应该够晦涩了吧!!

      编辑:它不是 0 结尾整数的双射

      900 <> 8100 <> 18 <> 2
      2   <> 18   <> 81 <> 9
      

      您始终可以添加特定规则,例如: 取一个数,除以 10 x 次,乘以 9,倒数,除以 9,乘以 10^x。

      所以

      900 <> 9 <> 81 <> 18 <> 2 <> 200
      200 <> 2 <> 18 <> 81 <> 9 <> 900
      

      W00t 它有效!

      编辑2:为了更模糊,您可以添加任意数字,并在末尾减去。

      900 < +256 > 1156 < *9 > 10404 < invert > 40401 < /9 > 4489 < -256 > 4233
      123 < +256 > 379 < *9 > 3411 < invert > 1143 < /9 > 127 < -256 > -129
      

      【讨论】:

      • 如何处理溢出?将一个大的 32 位数字乘以 9 可以得到 > 2^32
      • 是的,溢出的小问题,2147483647 3647263599。该算法保证了集合 10^N 上的双射,而不是 2^N,这是一个很小的细节:p
      • 正如我上面所写,我正在寻找一种可以生成任意外观输出的密码,但您的解决方案会为序列号生成类似的输出:123=>779、124=>679、125=> 579...默默无闻对我来说是必须的,但不是唯一的问题。出于这个原因,我不使用简单的 XOR 密码或任何其他对称密码,这可以产生更好的结果(就模糊性而言)
      • 我在想,也许在倒数上运行这个算法两次,甚至更多,10 次,结果可能会很有趣。我会尝试编写一些代码!
      【解决方案8】:

      这是我的简单想法: 您可以按照 PeterK 的建议移动数字的位,但您可以为每个数字设置不同的位排列,并且仍然能够破译它。

      密码是这样的: 将输入数字视为位数组I[0..31],将输出视为O[0..31]。 准备一个包含 64 个随机生成数字的数组 K[0..63]。这将是你的钥匙。 从第一个随机数 (I[K[0] mod 32]) 确定的位置获取输入数字的位,并将其放在结果的开头 (O[0])。现在要决定将哪个位放置在O[1],使用之前使用的位。如果为0,则使用K[1]在I中生成位置,从中获取,为1,使用K[2](即跳过一个随机数)。

      现在这将无法正常工作,因为您可能会两次使用相同的位。为了避免这种情况,在每次迭代后重新编号位,省略使用的位。要生成从 O[1] 获取的位置,请使用 I[K[p] mod 31],其中 p 是 1 或 2,具体取决于位 O[0],因为还剩下 31 位,编号从 0 到 30。

      为了说明这一点,我举个例子:

      我们有一个 4 位数字和 8 个随机数:25、5、28、19、14、20、0、18。

      I: 0111    O: ____
          _
      

      25 mod 4 = 1,所以我们取位置为 1 的位(从 0 开始计数)

      I: 0_11    O: 1___
           _
      

      我们刚刚取了一点值 1,所以我们跳过一个随机数并使用 28。剩下 3 位,所以要计算位置,我们取 28 mod 3 = 1。我们取第一个(从 0 开始计数) ) 的剩余位:

      I: 0__1    O: 11__
         _
      

      我们再次跳过一个数字,取 14. 14 mod 2 = 0,所以我们取第 0 位:

      I: ___1    O: 110_
            _
      

      现在没关系了,但是前面的位是0,所以我们取20。20 mod 1 = 0:

      I: ____    O: 1101
      

      就是这样。

      解密这样一个数字很容易,只需要做同样的事情。从密钥中可以知道放置代码第一位的位置,下一个位置由之前插入的位确定。

      这显然具有仅移动位的所有缺点(例如,0 变为 0,MAXINT 变为 MAXINT),但似乎更难找到某人如何在不知道密钥的情况下加密数字,这必须保密。

      【讨论】:

        【解决方案9】:

        如果您不想使用正确的加密算法(可能出于性能和复杂性的原因),您可以改用更简单的密码,例如 Vigenère cipher。这种密码实际上被描述为 le chiffre indéchiffrable(法语为“牢不可破的密码”)。

        这是一个简单的 C# 实现,它根据相应的键值移动值:

        void Main()
        {
          var clearText = Enumerable.Range(0, 10);
          var key = new[] { 10, 20, Int32.MaxValue };
          var cipherText = Encode(clearText, key);
          var clearText2 = Decode(cipherText, key);
        }
        
        IEnumerable<Int32> Encode(IEnumerable<Int32> clearText, IList<Int32> key) {
          return clearText.Select((i, n) => unchecked(i + key[n%key.Count]));
        }
        
        IEnumerable<Int32> Decode(IEnumerable<Int32> cipherText, IList<Int32> key) {
          return cipherText.Select((i, n) => unchecked(i - key[n%key.Count]));
        }
        

        当输入稍微改变时,该算法不会在输出中产生大的变化。但是,您可以使用另一个双射运算而不是加法来实现。

        【讨论】:

          【解决方案10】:

          在一张大纸上画一个大圆圈。从圆的顶部顺时针写出从 0 到 MAXINT 的所有整数,间隔相等。逆时针写从 0 到 MININT 的所有整数,再次等距。观察圆圈底部的 MININT 位于 MAXINT 旁边。现在在一张硬卡片的两侧复制这个图形。通过两者的中心将硬卡固定在圆圈上。选择一个旋转角度,任何你喜欢的角度。现在您有一个 1-1 映射,它可以满足您的一些要求,但可能还不够晦涩难懂。解开卡片,将其翻转一个直径,任何直径。重复这些步骤(以任何顺序),直到您得到满意的双射。

          如果您一直密切关注,用您喜欢的语言编写这个程序应该不难。

          澄清在评论之后:如果您只是将卡片旋转到纸上,那么方法就像您抱怨一样简单。但是,当您翻转卡片时,任何m 的映射都不等于(x+m) mod MAXINT。例如,如果您不旋转卡片并将其围绕直径翻转 0(位于钟面的顶部),则 1 映射到 -1,2 映射到 -2,依此类推。 (x+m) mod MAXINT 只对应卡片的旋转。

          【讨论】:

          • 据我了解,您提到的函数可以定义为 f(x)=(x+m) mod MAXINT 其中 0
          【解决方案11】:

          将数字分成两部分(16 个最高有效位和 16 个最低有效位),并将两个 16 位结果中的位视为两副牌中的牌。将牌组混合在一起,迫使其中一个进入另一个。

          因此,如果您的初始号码是 b31,b30,...,b1,b0,那么您最终会得到 b15,b31,b14,b30,...,b1,b17,b0,b16。实施起来又快又快,反之亦然。

          如果你看一下结果的十进制表示,这个系列看起来很模糊。

          您可以手动映射 0 -> maxvalue 和 maxvalue -> 0 以避免它们映射到自己。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-09-21
            • 2016-01-08
            • 1970-01-01
            • 2011-04-10
            相关资源
            最近更新 更多