为Swift生成十进制输出的哈希

时间:2019-02-28 16:55:38

标签: swift cryptography

我想将String散列到具有一些数字值NSNumber / Int而不是字母数字值的散列对象中。

问题是在快速浏览了一些第三方库之后,我找不到能够满足我们需求的任何库。

我正在使用聊天SDK,它使用NSNumber / Int作为唯一标识符来关联聊天消息和对话消息。

  

我公司的要求是不将任何附加字段存储到数据库中   或更改使事情复杂化的模式。

我的团队提供的一个整洁的解决方案是某种生成数字的哈希函数。

func userIdToConversationNumber(id:String) -> NSNumber

我们可以使用该函数将String转换为NSNumber / IntInt应该由该函数产生,并且发生碰撞的可能性应微不足道。关于任何方法的任何建议。

2 个答案:

答案 0 :(得分:2)

您需要执行的关键计算是生日限制。我最喜欢的表是Wikipedia中的表,在设计这样的系统时会定期引用它。

该表表示在您期望发生冲突之前,可以为给定的哈希大小哈希多少个项目。这是基于完全均匀的哈希,而密码哈希是其近似值。

因此,对于64位整数,在对6M个元素进行哈希处理之后,该列表中的任意位置发生单个冲突的可能性为百万分之一。在对20M个元素进行哈希处理之后,发生一次碰撞的几百分之一的可能性。在经过50亿个元素后,您应该押注一次碰撞(50%的机会)。

因此,这全都取决于您计划哈希多少元素以及如果发生冲突有多糟糕(这会造成安全问题吗?是否可以检测到它?您可以对它做任何事情,例如更改输入数据) ?),当然,您愿意为给定的问题承担多少风险。

就这些事情而言,我个人是百万分之一的人,尽管我被说服有时会降到千分之一。 (再次,这不是任何给予元素发生碰撞的1:1000机会;这太可怕了。这是1:1000散列后完全发生 碰撞的机会某些元素。)在攻击者可以制作任意大小(任意大小)的东西供您哈希的情况下,我不会接受百万分之一的条件。但是我对长度受限制的结构化数据(电子邮件地址,URL)非常满意。

如果这些数字对您有用,那么您想要的是散列在所有位上高度统一的哈希。这就是SHA哈希。我将使用SHA-2(例如SHA-256),因为除非有充分的理由,否则您应该始终使用SHA-2。由于SHA-2的位彼此独立(或至少是其意图),因此you can select any number of its bits to create a shorter hash。因此,您要计算SHA-256,并将顶部(or bottom)64位作为整数,这就是您的哈希值。

通常,对于中等大小的东西,您可以用64位来解决。您无法在32位上解决这个问题。因此,当您说“ NSNumber / Int”时,我希望您明确表示“ 64位整数”。例如,在32位平台上,Swift的Int只有32位,因此我将使用UInt64或uint64_t,而不是Int或NSInteger。我建议在这里使用无符号整数,因为它们实际上是唯一的位模式,而不是“数字”(即,将它们相加或相乘没有意义),并且除非标识符具有某种语义含义,否则具有负值的值在标识符中会造成混淆。 >

请注意,如果哈希是由加密随机数生成器生成的,则此处关于哈希的所有说明也适用于随机数。实际上,对于这类问题,我通常使用随机数。例如,如果我希望客户端为消息生成自己的随机唯一ID,那么我需要多少位来安全地避免冲突? (在我的许多系统中,您可能无法使用值中的所有位;有些可能用作标志。)

这是我的一般解决方案,但是如果您的输入空间受到限制,那么还有一个更好的解决方案。如果您的输入空间小于2 ^ 64,那么您根本不需要哈希。显然,任何最多8个字符的Latin-1字符串都可以存储在64位值中。但是,如果您的输入受到更多限制,则可以压缩数据并获得稍长的字符串。只需5位即可对26个符号进行编码,因此,如果您愿意进行数学运算,则可以在UInt64中存储12个字母字符串(单个拉丁字母)。您很幸运地有幸使用此功能,这是非常罕见的,但是当空间有限时,值得记住的是。

我已经建立了很多这样的系统,并且我会说,最终,我们几乎总是结成一个更长的标识符。您可以使它在一个较小的标识符上工作,但总会有些复杂,并且没有什么比拥有更多位更有效的了。。。

答案 1 :(得分:0)

是的,您可以使用加密散列函数创建抗冲突的哈希。如果遵循算法规范,则此类哈希函数的输出将以位为单位。但是,实现通常将仅返回字节或字节值的编码。哈希不会返回数字,就像其他人在注释中指出的那样。

将这样的散列转换为32位,例如IntInt32相对容易。您只需要获取哈希的最左字节并将其解释为无符号整数即可。

但是,加密散列具有相对较大的输出大小 ,以确保冲突的机会较小。冲突容易产生生日问题,这意味着您只需要尝试2的hLen幂除以2输入即可在生成的集合中创建冲突。例如。您将需要2 ^ 80次尝试创建RIPEMD-160哈希值的冲突。

现在,对于大多数密码散列(当然是常见的散列)而言,相同的规则也很重要。这意味着对于32位哈希,您只需要2 ^ 16哈希就可以合理地确定发生冲突。这不好,65536次尝试非常容易完成。有人可能会很幸运,例如在256次尝试之后,您有十分之一的机会发生碰撞。那不好。

因此计算散列值以将其用作ID很好,但是您需要散列函数的完整输出,例如256位SHA-2,确保您没有碰撞。否则,您可能需要改用序列号代替行号。