【问题标题】:Crypto algorithm varying based on password itself?加密算法因密码本身而异?
【发布时间】:2014-07-09 07:23:16
【问题描述】:

我试过搜索,但一无所获(如果它们已经是东西,不知道这些会被称为什么,所以搜索有点困难),所以如果这是愚蠢的或已经在某个地方回答过,请原谅我。为了争论,当我说我正在散列某些东西时,我们说我正在使用 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 发现它是完全可逆的和散列,现在需要取消散列两次 - 一次使用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;
}

现在,在 5 次迭代中,每次迭代有 3 次 unhashes,产生 243 个候选密码,并且可能要消除数十个误报,但即使没有,也必须做 243 倍于你刚才所做的 unhashes 工作完成了。此外,在后续哈希中再次将输入作为盐包含在内,使得实际上无法进行反哈希,而且它需要攻击者保留一点额外的内存。也就是说,我最后的想法如下:

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;
}

现在攻击者必须处理大量潜在的反散列(3^16 = 超过 400 万),每一个都必须使用上述内存密集型进行验证(它持有 16 个中间散列并且没有办法优化)。

其次,我觉得最后一个示例的内存密集度与分支盐配对,甚至可能其中一个分支调用 bcrypt 两次而不是一次这一事实,在某种组合下,可能会使事情更难用图形暴力破解通过使手头的大头钉不适合它们或使过程浪费比正常情况更多的 I/O。如果不出意外,将此方法扩展到 16 次以上迭代将继续增加 RAM 使用量,使其更难并行化。想象一下,如果使用了 256 次迭代,并且必须为攻击中泄露的每个散列保留 1024 个中间散列的空间——如果中间散列本身是 1024 位(= 128 字节),那么每个散列浪费了 32kB 的内存。蛮力攻击的迭代,虽然不多,但对于蛮力攻击者来说,它肯定会增加至少稍微慢一点的迭代和更少的并行迭代(由于额外的内存 - 尽管 32kB 对现代密码破解设备,将 32GB 写入内存以进行一百万次猜测,如果没有其他内容,这应该会额外减慢速度)。

那么,我是在做某事,还是这完全是愚蠢的?

【问题讨论】:

标签: hash cryptography


【解决方案1】:

这更恰当地属于加密货币,但这是我的 0.02 美元:

您的计算略有偏差,取决于您所做的一些假设,但不一定成立。

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

“多盐”的想法在现实中几乎没有什么好处,因为盐通常以明文形式存储在明文密码旁边。

这是您认为“好吧,如果 1 个函数调用和 1 个盐是好的,那么 50 次迭代和 2 个盐更好。这不可能有伤害!”的事情之一。但这不是密码学的工作原理,这种事情可能会受到伤害,尽管在这种情况下不会;只是浪费资源。

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

【讨论】:

  • 我的建议绝对只是为了让背景比我自己更相关的人说话——我会站出来说没有人应该尝试使用上面的代码作为模板真正的哈希算法,直到有适当知识的人批准为止。
【解决方案2】:

A) 对手显然准备以非常低的成本计算最常用的 bcrypt()。 B)也许还准备运行 N 次简单的迭代,但有些作弊,比我们预期的要便宜。 C) 很可能无法反转散列函数。 在任何情况下,使用 bcrypt() 的“外部”过程都应该非常愚蠢地编写以降低强度。分支使得攻击者更难使用它的标准破解装置:当然对于 A) 和 B),也可能对于 C) - 但在 C) 的情况下,无论如何都会有大问题。

结合不同类型的加密哈希函数,应用分支,除非应用了一些微不足道的错误,否则它不会变弱;但对手很可能更难使用标准破解装置。如果您正在构建的基本哈希存在根本缺陷(许多人认为如果是这种情况他们会知道:但这只是天真)分支可能无法解决它。但可能会使破解变得更难而不是更容易。在这种情况下浪费资源是好的:与“愚蠢地多次应用它”相比,分支和不同的程序会比诚实的资源浪费更多的对手资源:太好了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-18
    • 1970-01-01
    • 2021-05-16
    • 2014-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多