Hash 密码算法根据密码本身而变化?

Hash 密码算法根据密码本身而变化?,hash,cryptography,Hash,Cryptography,我尝试过搜索,但没有发现任何东西(如果它们已经是东西,我不知道它们会被称为什么,所以搜索有点困难),所以请原谅我,如果这是愚蠢的,或者已经在某个地方得到了回答。为了便于讨论,当我说我在散列某些内容时,让我们说我在使用bcrypt或类似的声誉/质量 首先,是否有原因使哈希算法不能随密码或其中间哈希而变化 public static byte[] myHash(byte[] input, byte[] saltA, byte[] saltB) { return input[0] % 2 == 0

我尝试过搜索,但没有发现任何东西(如果它们已经是东西,我不知道它们会被称为什么,所以搜索有点困难),所以请原谅我,如果这是愚蠢的,或者已经在某个地方得到了回答。为了便于讨论,当我说我在散列某些内容时,让我们说我在使用bcrypt或类似的声誉/质量

首先,是否有原因使哈希算法不能随密码或其中间哈希而变化

public static byte[] myHash(byte[] input, byte[] saltA, byte[] saltB) {
  return input[0] % 2 == 0
    ? bcrypt(bcrypt(input, saltA), saltB)
    : bcrypt(bcrypt(input, saltB), saltA);
}
我觉得这不会占用太多CPU—这只是bcrypt的两次迭代,我在其他安全讨论中建议进行10多次迭代—但假设bcrypt已经发现完全可逆,如果您知道salt和散列,一次解包将需要解包两次—一次使用
saltA
,然后使用
saltB
,反之亦然,给你两个候选密码,其中一个是假阳性率为50%的诱饵(也就是说,由于它的第一位是正确的偶数或奇数,它会重新散列到正确的散列),需要启发式或人眼来正确识别真实密码,因此,您所需的计算资源至少增加了一倍,并且可能需要人工干预。但我们可以做得更好:

public static byte[] myBetterHash(byte[] input, byte[] saltA, byte[] saltB) {
  byte[] curr = input;
  for (int i = 0; i < 5; i++) {
    switch(input[i] % 3) {
      case 0: curr = bcrypt(bcrypt(bcrypt(curr, input), saltA), saltB); break;
      case 1: curr = bcrypt(bcrypt(bcrypt(curr, saltB), input), saltA); break;
      case 2: curr = bcrypt(bcrypt(bcrypt(curr, saltA), saltB), input); break;
    }
  }
  return input;
}
公共静态字节[]myBetterHash(字节[]输入,字节[]saltA,字节[]saltB){
字节[]curr=输入;
对于(int i=0;i<5;i++){
开关(输入[i]%3){
案例0:curr=bcrypt(bcrypt(bcrypt(curr,input),saltA),saltB);break;
案例1:curr=bcrypt(bcrypt(bcrypt(curr,saltB),input),saltA);break;
案例2:curr=bcrypt(bcrypt(bcrypt(curr,saltA,saltB),输入);中断;
}
}
返回输入;
}
现在,在5次迭代中,每次迭代有3次取消刷新,生成243个候选密码,可能要消除几十个误报,但即使没有,也必须执行243倍于刚刚完成的取消刷新工作。此外,在随后的散列中再次将输入作为salt包含,这使得不可能真正执行清除操作,而且需要攻击者保留一点额外的内存。尽管如此,我的最后一个想法如下:

public static byte[] myBestHash(byte[] input, byte[] saltA, byte[] saltB) {
  byte[] curr = input;
  byte[][] arr = new byte[16][]
  for (int i = 0; i < 16; i++) {
    arr[i] = curr;
    switch(curr[0] % 4) {
      case 0: curr = bcrypt(curr, saltA); break;
      case 1: curr = bcrypt(curr, saltB); break;
      case 2: curr = bcrypt(curr, arr[input[i] % i]); break;
      case 3: curr = bcrypt(bcrypt(curr, saltA), saltB); break;
    }
  }
  return input;
}
public static byte[]myBestHash(byte[]input,byte[]saltA,byte[]saltB){
字节[]curr=输入;
字节[][]arr=新字节[16][]
对于(int i=0;i<16;i++){
arr[i]=货币;
开关(电流[0]%4){
案例0:curr=bcrypt(curr,saltA);break;
案例1:curr=bcrypt(curr,saltB);中断;
案例2:curr=bcrypt(curr,arr[input[i]%i]);中断;
案例3:curr=bcrypt(bcrypt(curr,saltA,saltB);break;
}
}
返回输入;
}
现在,攻击者必须处理大量潜在的解释器(3^16=超过400万),每个解释器都必须使用上述内存密集型进行验证(它包含16个中间散列,无法对其进行优化)

第二,我觉得最后一个例子的记忆强度与分支盐相结合,甚至一个分支调用bcrypt两次而不是一次的事实,在某种组合中,通过使手头的大头钉不适合图形卡,或使处理过程比正常情况浪费更多的I/O,使图形卡更难使用暴力。如果没有其他原因的话,将这种方法扩展到16次迭代之后将继续增加RAM的使用,使并行化变得更加困难。想象一下,如果使用256次迭代,并且每次攻击中泄漏的哈希都必须保留1024个中间哈希的空间-如果中间哈希本身是1024位(=128字节),那么暴力攻击每次迭代都会浪费32kB的内存,这并不多,但对于蛮力攻击者来说,它肯定会增加至少稍微慢一点的迭代次数,并行迭代次数也会减少(由于额外的内存,虽然32kB对于现代密码破解装备来说并不算多,但它是32GB写入内存以供一百万次猜测,如果没有其他东西可以让事情稍微慢一点的话)


那么,我是在做什么,还是这太愚蠢了?

这更适合加密,但这是我的$0.02:

你的计算有点偏差,取决于你做出的一些假设,但这些假设并不一定成立

现在,额外迭代的想法并不新鲜——以非常类似的方式使用迭代,
bcrypt
已经在内部使用迭代

“多盐”的想法在现实中没有什么好处,因为盐通常以明文形式与明文密码一起存储

这是你认为“如果一个函数调用和一个salt是好的,那么50次迭代和两个salt就更好了。这不可能有什么害处!”但这不是密码学的工作方式,这种事情可能会有害处,尽管在这种情况下没有害处;这只会浪费资源


请不要盲目地做这种事。如果您想增强对针对哈希的暴力字典攻击的抵抗力,请调整
bcrypt
难度系数。同样地,选择一个长而独特的salt,让
bcrypt
做它该做的事。

这更适合加密,但这是我的$0.02:

你的计算有点偏差,取决于你做出的一些假设,但这些假设并不一定成立

现在,额外迭代的想法并不新鲜——以非常类似的方式使用迭代,
bcrypt
已经在内部使用迭代

“多盐”的想法在现实中没有什么好处,因为盐通常以明文形式与明文密码一起存储

这是你认为“如果一个函数调用和一个salt是好的,那么50次迭代和两个salt就更好了。这不可能有什么害处!”但这不是密码学的工作方式,这种事情可能会有害处,尽管在这种情况下没有害处;这只会浪费资源

请不要做这种事