代码之家  ›  专栏  ›  技术社区  ›  dreeves

数字的ROT13

  •  12
  • dreeves  · 技术社区  · 15 年前

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

    理念 rot13 是为了模糊文字,例如为了防止破坏。它并不意味着密码安全,只是简单地确保只有那些确信自己想读的人才会读它。

    我想做一些类似的数字,为涉及密封投标申请。大致上,我想给某人发送我的号码,并相信他们可以选择自己的号码,不受我的影响,但当他们准备好时,他们应该能够显示我的号码(纯粹是客户端)。他们不应要求我或任何第三方提供进一步的信息。

    (补充:注意假设收件人是可信的,不会作弊。)

    它不像rot13那么简单,因为某些数字,如1和2,会经常重复出现,你可能会记得,比如34.2实际上是1。

    以下是我特别想要的:

    函数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.
    
    13 回复  |  直到 15 年前
        1
  •  7
  •   Jon    15 年前

    您可以将您的数字作为一个4字节的浮点与另一个随机浮点组合成一个双精度浮点并发送。然后,客户机只需接收前四个字节。在蟒蛇中:

    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
  •  6
  •   dreeves    15 年前

    这是一个从斯万特的回答中得到启发的解决方案。

    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(如果这是5000或更多,那么它是一个负数,所以减去另一个9999)。同样好的是,模糊的出价最长为6位数字。(这是我所考虑的足够的安全性——如果出价可能超过5000美元,那么我会使用更安全的方法。当然,此方法中的最大出价可以设置为您想要的最高值。)

    民间指导

    选择一个介于9和99之间的数字,并乘以9999,然后添加您的出价。 这将产生一个5或6位数字编码您的出价。 要取消密封,除以9999,减去小数点左边的部分,然后乘以9999。 (儿童和数学家都知道这一点,即“除以9999求余数”或“mod'ing除以9999”。)

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

    同样,请注意,这是在荣誉系统上的:没有任何技术上阻止你一看到对方的号码就打开它。

        3
  •  4
  •   John Rasch    15 年前

    如果您依赖于用户的诚实,并且只处理整数投标,那么您只需要一个带有随机数的简单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
  •  4
  •   Svante    15 年前

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

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

    然后接收者只需解码并找到剩余的模 最大出价 .

        5
  •  4
  •   Craig Gidney Mihai    15 年前

    你想做什么(a Commitment scheme )只做客户端是不可能的。最好的方法是使用共享密钥加密。

    如果客户不需要你的合作来透露号码,他们可以修改程序来透露号码。你也可以直接发送而不显示它。

    为了正确地完成这项工作,您可以发送一个安全的投标哈希+一个随机的salt。承诺你的出价。另一个客户可以用同样的方式承诺他们的出价。然后你们每个人分享你们的出价和盐。

    [编辑]因为您信任另一个客户:

    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)
    
        6
  •  2
  •   Macke    15 年前

    你知道你需要一个比原来更大的“密封”数字集吗,如果你想工作的话?

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

        7
  •  2
  •   Steve Jessop    15 年前

    一个简单的方法是写一条消息,比如:

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

    所有的垃圾都是随机产生的,每次都不一样。

    将消息的sha256散列发送给另一个人。让他们把他们的报价单寄给你。然后,一旦你们都有了散列,发送完整的消息,并确认他们的出价与他们给你们的散列相符。

    这提供了比你需要的更有力的保证——事实上,在你给他们完整的信息之前,他们是不可能制定出你的出价的。但是,没有您描述的unseal()函数。

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

    如你所说,如果你希望他们能够恢复你的出价而不需要你的任何进一步的输入,你愿意相信他们只是在发布他们的出价后才这样做,那么只需使用任何旧的对称密码加密。( gpg --symmetric 可能)和键“rot13”。这将防止意外作弊,但允许不可检测的作弊。

        8
  •  1
  •   hlovdal    15 年前

    我突然想到的一个想法是也许把你的算法建立在数学基础上 用于安全密钥共享。

    如果你想给鲍勃和爱丽丝两个人,每人半把钥匙。 只有当它们组合在一起时,它们才能打开任何钥匙锁,你是怎么做到的?解决这个问题的方法来自数学。假设在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值。 (确定性的)但有一个点不时变化。

    这样密封(B点的Y值)将根据A点给出完全不同的结果, 但是unseal(seal(b点的y值))应该返回b的y值,这就是你想要的。

    聚苯乙烯 不需要在y轴的不同边上有a和b,但是概念上用这种方式来考虑它要简单得多(我也建议用这种方式来实现它)。

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

        9
  •  1
  •   Evert    15 年前

    伪代码:

    编码:

    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
  •  1
  •   vezult    15 年前

    因为你似乎在假设另一个人没有 希望 要知道你的出价,直到他们自己提出,并可以信任不欺骗,你可以尝试一个可变的轮换方案:

    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'
    

    在某种程度上(我认为我们可能已经通过了),这变得很愚蠢,因为这很容易,所以您应该使用简单的加密,在这个加密中,消息接收者总是知道密钥——可能是此人的用户名。

        11
  •  0
  •   Michael Myers KitsuneYMG    15 年前

    如果出价是相当大的数字,那么一个具有预先确定的随机ISH数字的位异或如何?XORing将再次检索原始值。
    只要客户机和服务器都知道,您可以随时更改数字。

        12
  •  0
  •   Jeffrey Berthiaume    15 年前

    你可以设置一个不同的基础(如16,17,18等),并跟踪你“密封”的投标与…

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

    你可能想使用17到36的基数(只是因为有些人可能会识别出十六进制并能在他们的大脑中翻译它…)

    这样,您就可以获得G4、Z3或Kw之类的数字(取决于您要密封的数字)。

        13
  •  0
  •   dreeves    15 年前

    以下是一个省钱的方法,可以利用ROT13:

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

    然后定义

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

    unseal(x) = rot13(x)
    

    当然,unseal的输出不是一个实际的数字,只对人类有用,但这可能是可以的。 你可以让它更复杂一点,单词到数字的功能,也会扔掉所有乱七八糟的单词(定义为任何不是数字单词之一的单词,我想少于100个)。

    清醒检查:

    > 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可用的话,这是一种“手工”的方法。