合适的密码哈希器和内存破坏者:Argon2

本文介绍了密码哈希算法Argon2,它是一种内存密集型的哈希算法,旨在抵抗GPU破解。文章解释了Argon2的原理、不同变体(Argon2d、Argon2i、Argon2id)及其参数,并提供了一个使用JavaScript实现的Argon2示例,展示了如何在浏览器中使用Argon2进行密码哈希。

适当的密码哈希器和内存破坏者:Argon2

你知道那些告诉你密码多久会被破解的表格吗?嗯,它们都有点傻,因为它们没有考虑到破解每个密码的难度。事实上,它们倾向于基于快速哈希方法来制定基准。一个更具代表性的表格是分析每种方法:

在这里,我们看到有快速哈希方法(如 NTLM、MD5 和 SHA1)和慢速哈希方法(如 PBKDF2、bcrypt 和 SHA512crypt)。对于九字符密码,使用 bcrypt(来自表格),将花费超过 180 万年,而使用 NTLM(如早期版本的 Windows 中使用的),只需 几个小时

那么,最佳的密码哈希方法是什么,它们是如何工作的?我们用于存储哈希密码的最常用方法是 PBKDF2、crypt 和 bcrypt。这些方法使用多轮哈希,这将减慢哈希密码的总体时间。例如,如果我们有 1,000 轮哈希,那么生成哈希值的时间将大约长 1,000 倍。但是 GPU 有多个核心,因此使用具有 4,000 个核心的 GPU,我们可以一次尝试多达 4,000 个密码,这加快了该过程。那么,我们如何破解这些 GPU 呢?嗯,一种方法是给它们一个难题,让它们努力工作。为此,GPU 的每个核心都有相对较少数量的本地(缓存)内存,并且,如果它们需要更多内存,则必须减慢其运行速度,并使处理器因内存传输而停滞。

Argon2

在网络安全中,有很多情况是你有一个密钥,然后需要从中派生出一些东西——以及一个 salt 值。这涉及到获取一个密码,然后派生出一个固定大小的加密密钥或一个哈希值。但是,我们遇到的问题是,GPU 允许快速哈希,因此通常可以快速破解密码的哈希版本。为了克服这个问题,我们通常对哈希过程应用多轮处理,例如 Bcrypt 和 PBKDF2。

不幸的是,GPU 核心仍然可以以合理的成本运行哈希过程。因此,为了克服使用 GPU 进行破解,我们可以构建一种计算方法,该方法将需要给定的内存量来完成任务。由于 GPU 的核心对于每个核心没有太多内存,因此本地内存会过载,并导致 GPU 减速。这被称为内存硬(memory-hard),并通过 Argon 2 实施。

除此之外,在加密货币等领域,我们还有一些应用程序,其中需要完成给定的计算量(工作量)才能执行任务。同样,我们可以使用 Argon 2 来定义计算的工作量要求。

Argon2 在 RFC 9106 中定义,标题为“用于密码哈希和工作量证明应用程序的内存硬函数(Memory-Hard Function for Password Hashing and Proof-of-Work Applications)”,用于密码哈希、密钥派生和工作量证明:

主要变体是为 x86 架构优化的 Argon2id。它有两个子变体:Argon2d 和 Argon2i。总体而言,Argon2d 提供了一种与数据相关的内存访问方法,并且对加密货币和工作量证明应用程序很有用。它对侧信道时序攻击也没有任何威胁。或者,Argon2i 使用与数据无关的内存访问,可用于密码哈希和基于密码的密钥派生 (KDF)。

Argon 2 中使用的主要参数如图 1 所示,例如迭代次数、memcost 和并行度。标准形式是:

$argon2i$v=19$m=4096,t=2,p=4$salt$rZHPyRIa8XEvQ9rVqpvoibllLagNNGUeCNCmxeZfgBA

其中 v=19 标识我们正在使用 Argon 2。然后参数在“m=4096,t=2,p=4”中定义,其中我们具有 4,096 字节的内存成本,并行度为 4,迭代次数为 2。 在本例中,salt 是“salt”,派生的哈希是“rZHPyRIa8XEvQ9rVqpvoibllLagNNGUeCNCmxeZfgBA”。Argon 2 还可以使用定义的字节数(摘要大小)输出。

图 1:Argon 2

参数为:

  • Password(密码)。这是要哈希的字符串。
  • Salt(盐)。这是一个范围值,需要至少八个字符/字节长。
  • Time Cost(时间成本)(Iterations(迭代))。这定义了哈希的迭代次数。
  • Memory Cost(内存成本)(RAM Usage(内存使用))。这定义了哈希的内存量(以 KiB 为单位)。内存成本必须至少是并行值的八倍。
  • Parallelism(并行度)。这是用于计算哈希的线程数。
  • Hash Length(哈希长度)。这是哈希的字节数,并且至少四个字节长。
  • Argon2 Type(Argon2 类型)。这定义了 Argon2 方法:Argon2d、Argon2i 和 Argon2id

一个例子是:

Type:       Argon2i
Message:    Hello 123
Time cost:  8
Mem cost:   64
Salt:       8f3fe0a782e42b6c
Parallelism:    4
Hash:       cc117e31
Hash:       $argon2i$v=19$m=64,t=8,p=4$jz/gp4LkK2w$zBF+MQ
Hash generated in 13 ms

代码

以下是纯 JavaScript 中的代码[ here]:

<script src="https://cdnjs.cloudflare.com/ajax/libs/argon2-browser/1.18.0/argon2-bundled.min.js
"></script>
<style>
    .dropdown {
        font-size: 16px;
        border: 2px solid grey;
        width: 100%;
        border-left: 12px solid green;
        border-radius: 5px;
        padding: 14px;
    }
    pre {
        font-size: 16px;
        border: 2px solid grey;
        width: 100%;
        border-left: 12px solid green;
        border-radius: 5px;
        padding: 14px;
    }
    textarea {
        font-size: 20px;
        border: 2px solid grey;
        width: 100%;
        border-radius: 5px;
        padding: 14px;
    }
</style>

<div class="indented">
    <table width="100%">
        <tr>
            <th width="15%">Method(方法)</th>
            <td style="text-align:left">
                <p>
                    <input id="genkey" class="btn btn-large btn-primary" type="button" value="Gen New Argon2(生成新的Argon2)" />
                </p>

            </td>
        </tr>
        <tr>
            <th width="15%">Message(信息)</th>
            <td>
                <textarea cols="20" id="message" name="message" rows="1" style="width:100%"></textarea>
            </td>
        </tr>

        <tr>
            <th width="15%">Time cost(时间成本)</th>
            <td style="text-align:left">
                <div class="dropdown">
                    <select name="timecost" id="timecost">
                        <option value="4">4</option>
                        <option value="5">5</option>
                        <option value="6">6</option>
                        <option value="7">7</option>
                        <option value="8">8</option>
                        <option value="9">9</option>
                        <option value="10">10</option>
                        <option value="11">11</option>
                    </select>
                </div>
            </td>
        </tr>
        <tr>
            <th width="15%">Mem Cost(内存成本)</th>
            <td style="text-align:left">
                <div class="dropdown">
                    <select name="memcost" id="memcost">
                        <option value="32">32</option>
                        <option value="64">64</option>
                        <option value="128">128</option>
                        <option value="256">256</option>
                        <option value="512">512</option>
                    </select>
                </div>
            </td>
        </tr>
        <tr>
            <th width="15%">Parallelism(并行度)</th>
            <td style="text-align:left">
                <div class="dropdown">
                    <select name="parallelism" id="parallelism">
                        <option value="1">1</option>
                        <option value="2">2</option>
                        <option value="3">3</option>
                        <option value="4">4</option>

                    </select>
                </div>
            </td>
        </tr>
        <tr>
            <th width="15%">Hash Length(哈希长度)</th>
            <td style="text-align:left">
                <div class="dropdown">
                    <select name="hashlength" id="hashlength">
                        <option value="4">4</option>
                        <option value="5">5</option>
                        <option value="6">6</option>
                        <option value="7">7</option>
                        <option value="8">8</option>
                        <option value="9">9</option>
                        <option value="10">10</option>
                        <option value="11">11</option>
                        <option value="12">12</option>
                        <option value="13">13</option>
                        <option value="14">14</option>
                    </select>
                </div>
            </td>
        </tr>
        <tr>
            <th width="15%">Salt Size (Bytes)(Salt 大小(字节))</th>
            <td style="text-align:left">
                <div class="dropdown">
                    <select name="saltlength" id="saltlength">
                        <option value="8">8</option>
                        <option value="9">9</option>
                        <option value="10">10</option>
                        <option value="11">11</option>
                        <option value="12">12</option>
                        <option value="13">13</option>
                        <option value="14">14</option>
                        <option value="15">15</option>
                        <option value="16">16</option>
                    </select>
                </div>
            </td>
        </tr>

        <tr>
            <th width="15%">Method(方法)</th>
            <td style="text-align:left">
                <div class="dropdown">
                    <select name="hashtype" id="hashtype">
                        <option value="Argon2i">Argon2i</option>
                        <option value="Argon2d">Argon2d</option>
                        <option value="Argon2id">Argon2id</option>
                    </select>
                </div>
            </td>
        </tr>

    </table>

    <h2>Hash</h2>
    <table width="100%">
        <tr>
            <th valign="top" width="15%">Hash</th>
            <td>
                <pre id="key"></pre>
            </td>
        </tr>

    </table>

</div>

<script>
    function toHexString(byteArray) {
        return Array.from(byteArray, function (byte) {
            return ('0' + (byte & 0xFF).toString(16)).slice(-2);
        }).join('')
    }
    (async function () {

        async function genKey() {
            var message = document.getElementById("message").value;
            var hashlength = parseInt(document.getElementById("hashlength").value);
            var saltlength = parseInt(document.getElementById("saltlength").value);
            var timecost = parseInt(document.getElementById("timecost").value);
            var memcost = parseInt(document.getElementById("memcost").value);
           var parallel = parseInt(document.getElementById("parallelism").value);
            var salt = window.crypto.getRandomValues(new Uint8Array(saltlength));
            var dt = new Date();
            var time = -(dt.getTime());
            var hashtype = document.getElementById("hashtype").value;
            var hashingtype;
            if (hashtype == "Argon2i") hashingtype = argon2.ArgonType.Argon2i;
            else if (hashtype == "Argon2d") hashingtype = argon2.ArgonType.Argon2d;
            else hashingtype = argon2.ArgonType.Argon2id;

           const hash = await argon2.hash({
                pass: message,
               salt: salt,
                time: timecost, // Time cost(时间成本)
                mem: memcost, // Memory cost in KB(KB 中的内存成本)
               parallelism: parallel, // Number of parallel threads(并行线程数)
                hashLen: hashlength, // Length of the hash(哈希长度)
               type: hashingtype,
            });
            console.log(hash.hashHex);
            dt = new Date();
            time += (dt.getTime());

            document.getElementById("key").innerHTML = "Type:\t\t" + hashtype;
            document.getElementById("key").innerHTML += "\nMessage:\t" + message;
            document.getElementById("key").innerHTML += "\nTime cost:\t" + timecost;
            document.getElementById("key").innerHTML += "\nMem cost:\t" + memcost;
            document.getElementById("key").innerHTML += "\nSalt:\t\t" + toHexString(salt);
            document.getElementById("key").innerHTML += "\nParallelism:\t" +parallel;
            document.getElementById("key").innerHTML += "\nHash:\t\t" + hash.hashHex;
            document.getElementById("key").innerHTML += "\nHash:\t\t" + hash.encoded;
            document.getElementById("key").innerHTML += '\nHash generated in ' + time + ' ms\n';

        }
        document.getElementById('message').value = "Hello 123";
        await genKey();
        document.getElementById("genkey").addEventListener("click", genKey);
        document.getElementById("timecost").addEventListener("click", genKey);
        document.getElementById("memcost").addEventListener("click", genKey);
        document.getElementById("hashlength").addEventListener("click", genKey);
        document.getElementById("saltlength").addEventListener("click", genKey);
        document.getElementById("hashtype").addEventListener("click", genKey);
        document.getElementById("parallelism").addEventListener("click", genKey);
        document.getElementById("message").addEventListener("input", genKey);
        message.focus();

    })();
</script>

浏览器中 Argon2i 的一些示例运行如下:

请注意,时间以毫秒为单位,其中 t 是时间成本。我们可以看到,如果时间成本为 8 且内存成本为 128MB,则将花费 1,467 毫秒。你可以在这里自己尝试一下:

Argon2 with JavaScript(使用 JavaScript 的 Argon2) \ \ Argon2 is defined in RFC 9106 and is entitled as, "Memory-Hard Function for Password Hashing and Proof-of-Work…(Argon2 在 RFC 9106 中定义,标题为“用于密码哈希和工作量证明的内存硬函数……”)\ \ asecuritysite.com

  • 原文链接: billatnapier.medium.com/...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
billatnapier
billatnapier
江湖只有他的大名,没有他的介绍。