【问题标题】:Rot13 for numbersRot13 表示数字
【发布时间】:2009-04-30 21:16:51
【问题描述】:

编辑:现在是 http://messymatters.com/sealedbids 的主要运动博客帖子

rot13 的想法是隐藏文本,例如防止剧透。它并不是为了加密安全,而只是为了确保只有确定要阅读它的人才能阅读它。

对于涉及密封投标的应用程序,我想对数字做类似的事情。大致上,我想将我的号码发送给某人,并相信他们会选择自己的号码,不受我的影响,但是当他们准备好时,他们应该能够透露我的号码(纯粹是客户端)。他们不应要求我或任何第三方提供进一步的意见。

(补充:请注意接受者被信任不会作弊的假设。)

它不像 rot13 那样简单,因为某些数字(例如 1 和 2)会经常出现,以至于您可能会记得,例如,34.2 确实是 1。

这是我正在寻找的具体内容:

将实数映射到实数(或字符串)的函数 seal()。它应该是确定性的—— seal(7) 不应该每次都映射到同一个东西。但是相应的函数 unseal() 应该是确定性的—— unseal(seal(x)) 应该等于所有 x 的 x。我不希望 seal 或 unseal 调用任何 web 服务甚至获取系统时间(因为我不想假设同步时钟)。 (补充:可以假设所有出价都低于某个最大值,每个人都知道,比如一百万。)

完整性检查:

> seal(7)
482.2382   # some random-seeming number or string.
> seal(7)
71.9217    # a completely different random-seeming number or string.
> unseal(seal(7))
7          # we always recover the original number by unsealing.

【问题讨论】:

  • 您提到要将实数发送到实数,但随后您发布了一个使用 rot-13 的示例解决方案(因此使用 ASCII 字符串而不是实数)。您感兴趣的领域和范围具体是什么?这些应该有多少熵(例如,范围内有多少可能的值应该对应于域中的一个值)?
  • 两位小数(多于)就足够了。在我基于 rot13 的解决方案中,您可以将 3.14 映射到“三点一四”。
  • 出价是否限制为整数 >= 0?
  • 好问题。令人惊讶的是,不,并非总是如此。否定出价在我们使用的机制中具有特殊意义。不过,如果您有一些在该约束下运行良好的东西,我有兴趣看到它。
  • 为什么要显示数字——比如说,显示一堆“xxxxxx”,直到用户点击“显示你自己”。 (虽然,如果这是一个打印过程......那么这确实是有道理的)

标签: language-agnostic math encryption function


【解决方案1】:

您可以将您的数字作为 4 字节浮点数与另一个随机浮点数打包成双精度并发送。然后客户端只需要获取前四个字节。在python中:

import struct, random
def seal(f):
   return struct.unpack("d",struct.pack("ff", f, random.random() ))[0]
def unseal(f):
   return struct.unpack("ff",struct.pack("d", f))[0]

>>> unseal( seal( 3))
3.0
>>> seal(3)
4.4533985422978706e-009
>>> seal(3)
9.0767582382536571e-010

【讨论】:

    【解决方案2】:

    这是一个受 Svante 回答启发的解决方案。

    M = 9999  # Upper bound on bid.
    seal(x) = M * randInt(9,99) + x
    unseal(x) = x % M
    

    完整性检查:

    > seal(7)
    716017
    > seal(7)
    518497
    > unseal(seal(7))
    7
    

    这需要调整以允许负出价:

    M = 9999  # Numbers between -M/2 and M/2 can be sealed.
    seal(x) = M * randInt(9,99) + x
    unseal(x) = 
      m = x % M; 
      if m > M/2 return m - M else return m
    

    这个解决方案的一个好处是接收者解码是多么微不足道 - 只需 9999 的 mod(如果是 5000 或更多,那么它是一个负出价,所以再减去 9999)。隐藏的出价最多为 6 位数,这也很好。 (这对于我的想法来说非常安全——如果出价可能超过 5000 美元,那么我会使用更安全的方法。当然,这种方法中的最高出价可以设置为你想要的最高值。)

    平信徒须知

    在 9 到 99 之间选择一个数字,然后乘以 9999,然后加上您的出价。 这将产生一个 5 或 6 位数字来编码您的出价。 要打开它,除以 9999,减去小数点左边的部分,然后乘以 9999。 (这被儿童和数学家分别称为“除以 9999 时求余数”或“除以 9999”。)

    这适用于小于 9999 的非负出价(如果这还不够,请使用 99999 或任意数量的数字)。 如果您想允许负出价,那么神奇的 9999 数字需要是最大可能出价的两倍。 并且在解码时,如果结果大于9999的一半,即5000或更多,则减去9999得到实际(负)出价。

    再次请注意,这是在荣誉系统中:技术上没有什么可以阻止您在看到对方的号码时立即打开它。

    【讨论】:

    • 优秀的答案。我们外行人欣赏它!
    【解决方案3】:

    如果您依赖用户的诚实并且只处理整数出价,那么您只需要使用随机数进行简单的 XOR 操作,C# 中的示例:

    static Random rng = new Random();
    
    static string EncodeBid(int bid)
    {
        int i = rng.Next();
        return String.Format("{0}:{1}", i, bid ^ i);
    }
    
    static int DecodeBid(string encodedBid)
    {
        string[] d = encodedBid.Split(":".ToCharArray());
        return Convert.ToInt32(d[0]) ^ Convert.ToInt32(d[1]);
    }
    

    用途:

    int bid = 500;
    string encodedBid = EncodeBid(bid); // encodedBid is something like 54017514:4017054 and will be different each time
    int decodedBid = DecodeBid(encodedBid); // decodedBid is 500
    

    将解码过程转换为客户端结构应该很简单。

    【讨论】:

      【解决方案4】:

      有最高出价吗?如果是这样,您可以这样做:

      max-bid 为最高出价,a-bid 为您要编码的出价。将max-bid 乘以一个相当大的随机数(如果你想在最后一步使用base64 编码,max-rand 应该是(2^24/max-bid)-1min-rand 可能是其中的一半),然后加上a-bid。对此进行编码,例如通过base64

      然后收件人只需解码并找到余数模 max-bid

      【讨论】:

      • 有一个最高出价,适用于我能想象到的任何应用(例如,对于实际的密封投标拍卖,世界 GDP 是一个上限)。但我认为您的方案存在问题,例如,出价为零将始终映射到具有相同最后一个字符的东西。
      • 不,仅当您使用在最后 6 位中没有设置位的最高出价时。如果是这种情况,您应该在实际最高出价中添加少量以获得良好的此方案的编号。
      • 啊,太好了!所以 seal(x) = 1234567*RandInt(1,100)+x;开封(x) = x % 1234567;杰出的。不过,它需要进行调整以处理负面出价。如果 maxbid 是实际最高出价的两倍,则应该可以修复。
      • 顺便说一句,我没有看到 base64 编码中的值。请参阅下面我对您的想法的实施。 (请随意将其合并到您的答案中,我会删除我的。)再次感谢;这很聪明。我认为这是迄今为止所有答案中最好的方法。
      • Svante,我可以知道你的真实姓名,这样我可以在提及/实施/写博客/等时给予你信任,你的想法?我认为它会非常方便——尤其是因为任何外行人都可以用手动计算器来完成。
      【解决方案5】:

      您想做的事情(Commitment scheme)不可能仅在客户端进行。您能做的最好的事情就是使用共享密钥进行加密。

      如果客户不需要你的合作来显示号码,他们可以修改程序来显示号码。您还不如只是发送它而不显示它。

      要正确执行此操作,您可以发送出价的安全哈希 + 随机盐。这会让你承诺你的出价。其他客户可以以同样的方式承诺他们的出价。然后你们各自分享你的出价和盐。

      [edit] 因为你信任另一个客户:

      Sender:
      Let M be your message
      K = random 4-byte key
      C1 = M xor hash(K) //hash optional: hides patterns in M xor K
      //(you can repeat or truncate hash(K) as necessary to cover the message)
      //(could also xor with output of a PRNG instead)
      C2 = K append M //they need to know K to reveal the message
      send C2 //(convert bytes to hex representation if needed)
      
      Receiver:
      receive C2
      K = C2[:4]
      C1 = C2[4:]
      M = C1 xor hash(K)
      

      【讨论】:

      • 当然。但请注意,我假设信任和善意,就像在 rot13 中一样。
      • 啊,这更像是;谢谢!尽管与此同时,我认为 Svante 的答案非常简单。
      【解决方案6】:

      如果您想让它起作用,您是否知道您需要比原来更大的“密封”数字集?

      所以你需要以某种方式限制你的真实数字,或者存储你没有显示的额外信息。

      【讨论】:

      • 我认为这只是理论上的问题,而不是实践中的问题。即使在理论上,无限域和范围也可能没问题。无论如何,我编辑了问题以允许映射到任意字符串,但我认为最好的答案并没有这样做。
      【解决方案7】:

      一个简单的方法是写这样的消息:

      “我的出价是:14.23 美元:aduigfurjwjnfdjfugfojdjkdskdfdhfddfuiodrnfnghfifyis”

      所有这些垃圾都是随机生成的,并且每次都不同。

      向对方发送消息的 SHA256 哈希值。让他们向您发送他们出价的哈希值。然后,一旦你们都有哈希值,发送完整的消息,并确认他们的出价与他们给你的哈希值一致。

      这提供了比您需要的更强有力的保证 - 实际上,在您向他们发送完整消息之前,他们不可能计算出您的出价。但是,没有您描述的 unseal() 函数。

      这个简单的方案具有完全零知识方案所没有的各种弱点。例如,如果他们通过向您发送随机数而不是散列来欺骗您,那么他们可以在不透露自己的情况下计算出您的出价。但你没有要求防弹。这可以防止意外和(我认为)无法检测到的作弊,并且只使用一个常用的命令行实用程序,外加一个随机数生成器(骰子就可以)。

      如您所说,如果您希望他们能够在没有您进一步输入的情况下恢复您的出价,并且您愿意相信他们只会在他们发布出价后这样做,那么只需使用任何旧的对称密码进行加密(gpg --symmetric,也许)和键,“rot13”。这将防止意外作弊,但允许无法察觉的作弊。

      【讨论】:

      • 具有静态密钥的对称密码不会每次都将相同的数字映射到相同的字符串吗?
      • 是的,我的意思是(但没有说)像我回答的第一部分那样加密加盐消息。
      • 哦,是的,应该选择垃圾的长度以将消息填充到固定长度。固定大小的盐可能接近密码块大小的倍数,结果出价超过 100 美元会输出更大的加密消息:-)
      【解决方案8】:

      我突然想到的一个想法是,你的算法可能基于数学 用于安全密钥共享。

      如果你想给 Bob 和 Alice 两个人各半个密钥,那么 只有当它们组合在一起时,它们才能打开任何钥匙锁,你是怎么做到的?解决这个问题的方法来自数学。假设您在 x/y 坐标系中有两个点 A (-2,2) 和 B (2,0)。

                     |
             A       +
                     |
                     C
                     |
      ---+---+---+---|---+---B---+---+---+---
                     |
                     +
                     |
                     +
      

      如果你在它们之间画一条直线,它将恰好在一个点 C (0,1) 处穿过 y 轴。 如果你只知道 A 点或 B 点之一,就不可能知道它会在哪里交叉。 因此,您可以让点 A 和 B 成为共享键,当它们组合时将显示 y 值 的交叉点(即本例中的 1),然后该值通常用作 真正的钥匙。

      对于您的投标应用,您可以让 seal() 和 unseal() 交换 C 点和 B 点之间的 y 值 (确定性),但 A 点会不时变化。

      这种方式密封(B点的y值)将根据A点给出完全不同的结果, 但是 unseal(seal(y-value of point B)) 应该返回 B 的 y 值,这就是您所要求的。

      PS 不需要将 A 和 B 放在 y 轴的不同侧,但从概念上来说,以这种方式考虑要简单得多(我建议也以这种方式实现)。

      通过这条直线,您可以在几个人之间共享密钥,这样只有两个人 他们需要解锁任何东西。可以使用直线以外的曲线类型来创建其他 密钥共享属性(即需要 3 个密钥中的 3 个等)。

      【讨论】:

        【解决方案9】:

        伪代码:

        编码:

        value = 2000
        key = random(0..255); // our key is only 2 bytes
        
        // 'sealing it'
        value = value XOR 2000;
        
        // add key
        sealed = (value << 16) | key
        

        解码:

        key = sealed & 0xFF
        unsealed = key XOR (sealed >> 16)
        

        这行得通吗?

        【讨论】:

          【解决方案10】:

          由于您似乎假设其他人在他们自己出价之前不知道您的出价,并且可以相信不会作弊,您可以尝试可变旋转方案:

          from random import randint
          
          def seal(input):
              r = randint(0, 50)
              obfuscate = [str(r)] + [ str(ord(c) + r) for c in '%s' % input ]
              return ':'.join(obfuscate)
          
          def unseal(input):
              tmp = input.split(':')
              r = int(tmp.pop(0))
              deobfuscate = [ chr(int(c) - r) for c in tmp ]
              return ''.join(deobfuscate)
          
          # I suppose you would put your bid in here, for 100 dollars
          tmp = seal('$100.00') # --> '1:37:50:49:49:47:49:49' (output varies)
          print unseal(tmp) # --> '$100.00'
          

          在某些时候(我想我们可能已经通过了)这会变得很愚蠢,因为它非常简单,你应该只使用简单的加密,消息接收者总是知道密钥——也许是这个人的用户名。

          【讨论】:

          • 将小数编码为base64?直觉上,这听起来像它根本不会做任何事情。还有更多我没有看到的吗?
          • 这是一个不错且简单的解决方案,但是您忘记了 seal(1) 每次都不应返回相同内容的约束。
          • 除非我误解了您的代码,否则 seal('$100') 虽然总是不同,但总是以 'JDEwM..." 开头。如果我发送该号码两次,那么收件人可能会注意到并知道又是“100 美元”。
          【解决方案11】:

          如果出价是相当大的数字,那么与一些预定的随机数进行按位异或怎么样?然后再次异或将检索原始值。
          只要客户端和服务器都知道,您可以随意更改数字。

          【讨论】:

          • 我不想假设大量数字(出价通常是 1 或 2 甚至 0),并且我想避免原始代码之外的协调问题。
          • 有趣的是,这被否决了,而前两个答案基本上暗示了同样的事情。
          • 我认为除非你有办法在它发生变化时将它传达给接收者,否则拥有一个预先确定的随机数是行不通的,它需要这样做,因为密封功能不能每次都将相同的数字映射到相同的输出。不过,您的想法可能使人们走上了正轨。 (尽管与此同时,我认为 Svante 的模数思想优于 XOR 方法,因为您不需要每次都学习新的幻数。)
          • 好吧,如果你看起来非常非常接近,我的最后一句话几乎说要传输随机数和加密数。 ;) 但是,是的,我确实认为 Svante 的想法是现在最好的。
          【解决方案12】:

          您可以设置不同的基数(例如 16、17、18 等),并跟踪您使用哪个基数“密封”出价...

          当然,这是假设大数(> 至少是您使用的基数)。如果它们是十进制,您可以删除该点(例如,27.04 变为 2704,然后您将其转换为基数 29...)

          您可能希望使用基数 17 到 36(只是因为有些人可能会识别十六进制并能够在他们的脑海中翻译它......)

          这样,您将拥有 G4 或 Z3 或 KW 之类的数字(取决于您要密封的数字)...

          【讨论】:

          • 如果有一些合理的最低出价,这是一个很好的解决方案。但在我的应用程序中,零实际上是一个常见的出价,我什至希望它被掩盖。
          【解决方案13】:

          这里有一个廉价的方式来搭载 rot13:

          假设我们有一个生成类似“fdjk alqef lwwqisvz”的函数 gibberish() 和一个将数字 x 转换为单词的函数 words(x),例如 words(42) 返回“四十二”(没有连字符) .

          然后定义

          seal(x) = rot13(gibberish() + words(x) + gibberish())
          

          unseal(x) = rot13(x)
          

          当然 unseal 的输出不是一个实际的数字,只对人类有用,但这可能没问题。 您可以使用 word-to-number 函数让它变得更复杂一些,它也会丢弃所有乱码(定义为任何不是数字词之一的东西——我认为少于一百个) .

          完整性检查:

          > seal(7)
          fhrlls hqufw huqfha frira afsb ht ahuqw ajaijzji
          > seal(7)
          qbua adfshua hqgya ubiwi ahp wqwia qhu frira wge
          > unseal(seal(7))
          sueyyf udhsj seven ahkua snsfo ug nuhdj nwnvwmwv
          

          我知道这很愚蠢,但如果你只有 rot13 可用,这是一种“手动”操作的方法。

          【讨论】:

          • 是的,但是如果您担心人们能够通过熟悉程度来解码数字,那么他们会知道“四十二”的 rot13 是什么样的,即使它会有些乱码在开头和结尾。
          • 您的出价为“四十”的可能性很小但不是很小,而后乱码将以“二”开头。因此,您需要例如过滤乱码以确保它不包含数字单词。
          • 好点,布赖恩。但也许有相当多的胡言乱语,重要的是,开头和结尾的长度都是随机的,你必须真正梳理它,我们假设人们不会这样做。 (再次假设善意——我只是不想在选择我的号码时受到你的影响。)
          • @onebyone:谢谢,非常正确!我认为 Svante 的想法确实做到了这一点,并使整个 rot13 hack 变得毫无意义。他的想法也可以手工完成。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-05-15
          • 2021-11-19
          • 1970-01-01
          • 2021-11-01
          相关资源
          最近更新 更多