这是 Argon2 的参考 C 实现。Argon2 是密码哈希函数,赢得了密码哈希竞赛 (PHC)。
Argon2 是一种密码哈希函数,总结了内存硬函数设计的最新技术,可用于对密码进行哈希以进行凭据存储、密钥派生或其他应用。
它具有一个简单的设计,旨在实现最高的内存填充率和多个计算单元的有效利用,同时仍通过利用最新处理器的缓存和内存组织来防御权衡攻击。
Argon2 有三个变体:Argon2i、Argon2d 和 Argon2id。Argon2d 更快,并使用数据相关的内存访问,这使其具有很强的抵抗 GPU 破解攻击的能力,并且适用于没有来自侧信道定时攻击的威胁的应用(例如,加密货币)。Argon2i 则使用数据独立的内存访问,这对于密码哈希和基于密码的密钥派生是首选,但速度较慢,因为它会在内存上进行更多传递,以防止权衡攻击。Argon2id 是 Argon2i 和 Argon2d 的混合体,它结合了数据相关和数据独立的内存访问,这使其具有 Argon2i 的一些抵抗侧信道缓存定时攻击的能力和 Argon2d 的大部分抵抗 GPU 破解攻击的能力。
Argon2i、Argon2d 和 Argon2id 由以下参数确定:
Argon2 文档 提供了详细的规范和设计原理。
请将错误作为 issue 在此存储库中报告。
make
构建可执行文件 argon2
、静态库 libargon2.a
和共享库 libargon2.so
(或者在 macOS 上,是动态库 libargon2.dylib
-- 确保在编译时指定安装前缀:make PREFIX=/usr
)。确保运行 make test
以验证你的构建是否产生有效结果。sudo make install PREFIX=/usr
将其安装到你的系统。
argon2
是一个命令行实用程序,用于在你的系统上测试特定的 Argon2 实例。要显示使用说明,请运行 ./argon2 -h
,例如:
Usage: ./argon2 [-h] salt [-i|-d|-id] [-t iterations] [-m memory] [-p parallelism] [-l hash length] [-e|-r] [-v (10|13)]
Password is read from stdin
Parameters:
salt The salt to use, at least 8 characters
-i Use Argon2i (this is the default)
-d Use Argon2d instead of Argon2i
-id Use Argon2id instead of Argon2i
-t N Sets the number of iterations to N (default = 3)
-m N Sets the memory usage of 2^N KiB (default 12)
-p N Sets parallelism to N threads (default 1)
-l N Sets hash output length to N bytes (default 32)
-e Output only encoded hash
-r Output only the raw bytes of the hash
-v (10|13) Argon2 version (defaults to the most recent version, currently 13)
-h Print argon2 usage
例如,使用 "somesalt" 作为 salt 对 "password" 进行哈希,进行 2 次迭代,消耗 64 MiB,使用四个并行线程,输出哈希值为 24 字节:
$ echo -n "password" | ./argon2 somesalt -t 2 -m 16 -p 4 -l 24
Type: Argon2i
Iterations: 2
Memory: 65536 KiB
Parallelism: 4
Hash: 45d7ac72e76f242b20b77b9bf9bf9d5915894e669a24e6c6
Encoded: $argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG
0.188 seconds
Verification ok
libargon2
提供了一个 API,用于使用 Argon2 的低级和高级函数。
下面的示例程序使用高级 API 和低级 API,使用 Argon2i 对字符串 "password" 进行哈希。高级 API 接受三个成本参数(时间、内存和并行度)、密码输入缓冲区、salt 输入缓冲区和输出缓冲区,而低级 API 接受这些参数以及 include/argon2.h
中定义的其他参数。
还有许多额外的参数,但我们将在此处重点介绍其中三个。
secret
参数,用于密钥哈希。 这允许在哈希时输入一个密钥(从某些外部位置),并将其折叠到哈希值中。 这意味着即使您的 salt 和哈希受到威胁,攻击者也无法在没有密钥的情况下进行暴力破解以查找密码。
ad
参数,用于将任何额外数据折叠到哈希值中。在功能上,它的行为几乎与 secret
或 salt
参数完全相同; ad
参数被折叠到哈希值中。但是,此参数用于不同的数据。 salt
应该是一个与您的密码一起存储的随机字符串。 secret
应该是一个仅在哈希时可用的随机密钥。 ad
用于任何其他数据。
flags
参数,它确定哪些内存应该被安全擦除。 如果您想在使用后立即安全地删除 pwd
或 secret
字段,这将非常有用。 为此,请将 flags
设置为 ARGON2_FLAG_CLEAR_PASSWORD
或 ARGON2_FLAG_CLEAR_SECRET
。 要更改内部内存的清除方式,请更改全局标志 FLAG_clear_internal_memory
(默认为清除内部内存)。
这里,时间成本 t_cost
设置为 2 次迭代,内存成本 m_cost
设置为 216 千字节(64 mebibytes),并行度设置为 1(单线程)。
如果下面的程序命名为 test.c
并放置在项目的根目录中,则可以例如编译为 gcc test.c libargon2.a -Isrc -o test
。
#include "argon2.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define HASHLEN 32
#define SALTLEN 16
#define PWD "password"
int main(void)
{
uint8_t hash1[HASHLEN];
uint8_t hash2[HASHLEN];
uint8_t salt[SALTLEN];
memset( salt, 0x00, SALTLEN );
uint8_t *pwd = (uint8_t *)strdup(PWD);
uint32_t pwdlen = strlen((char *)pwd);
uint32_t t_cost = 2; // 2-pass computation
uint32_t m_cost = (1<<16); // 64 mebibytes memory usage
uint32_t parallelism = 1; // number of threads and lanes
// high-level API
argon2i_hash_raw(t_cost, m_cost, parallelism, pwd, pwdlen, salt, SALTLEN, hash1, HASHLEN);
// low-level API
argon2_context context = {
hash2, /* output array, at least HASHLEN in size */
HASHLEN, /* digest length */
pwd, /* password array */
pwdlen, /* password length */
salt, /* salt array */
SALTLEN, /* salt length */
NULL, 0, /* optional secret data */
NULL, 0, /* optional associated data */
t_cost, m_cost, parallelism, parallelism,
ARGON2_VERSION_13, /* algorithm version */
NULL, NULL, /* custom memory allocation / deallocation functions */
/* by default only internal memory is cleared (pwd is not wiped) */
ARGON2_DEFAULT_FLAGS
};
int rc = argon2i_ctx( &context );
if(ARGON2_OK != rc) {
printf("Error: %s\n", argon2_error_message(rc));
exit(1);
}
free(pwd);
for( int i=0; i<HASHLEN; ++i ) printf( "%02x", hash1[i] ); printf( "\n" );
if (memcmp(hash1, hash2, HASHLEN)) {
for( int i=0; i<HASHLEN; ++i ) {
printf( "%02x", hash2[i] );
}
printf("\nfail\n");
}
else printf("ok\n");
return 0;
}
要使用 Argon2d 代替 Argon2i,请在使用高级 API 时调用 argon2d_hash_raw
而不是 argon2i_hash_raw
,在使用低级 API 时调用 argon2d
而不是 argon2i
。对于 Argon2id,类似地,调用 argon2id_hash_raw
和 argon2id
。
要生成类似 crypt 的编码而不是原始哈希,对于 Argon2i,请调用 argon2i_hash_encoded
,对于 Argon2d,请调用 argon2d_hash_encoded
,对于 Argon2id,请调用 argon2id_hash_encoded
。
有关 API 详细信息,请参见include/argon2.h
。
注意:在此示例中,为了简单起见,salt 设置为全 0x00
字符串,但在你的应用程序中,你应该使用随机 salt。
make bench
创建可执行文件 bench
,它测量各种 Argon2 实例的执行时间。
$ ./bench
Argon2d 1 iterations 1 MiB 1 threads: 5.91 cpb 5.91 Mcycles
Argon2i 1 iterations 1 MiB 1 threads: 4.64 cpb 4.64 Mcycles
0.0041 seconds
Argon2d 1 iterations 1 MiB 2 threads: 2.76 cpb 2.76 Mcycles
Argon2i 1 iterations 1 MiB 2 threads: 2.87 cpb 2.87 Mcycles
0.0038 seconds
Argon2d 1 iterations 1 MiB 4 threads: 3.25 cpb 3.25 Mcycles
Argon2i 1 iterations 1 MiB 4 threads: 3.57 cpb 3.57 Mcycles
0.0048 seconds
(...)
Argon2d 1 iterations 4096 MiB 2 threads: 2.15 cpb 8788.08 Mcycles
Argon2i 1 iterations 4096 MiB 2 threads: 2.15 cpb 8821.59 Mcycles
13.0112 seconds
Argon2d 1 iterations 4096 MiB 4 threads: 1.79 cpb 7343.72 Mcycles
Argon2i 1 iterations 4096 MiB 4 threads: 2.72 cpb 11124.86 Mcycles
19.3974 seconds
(...)
以下语言提供了绑定(请确保阅读它们的文档):
有两组测试套件。一个是哈希函数的低级测试,另一个测试更高级的 API。 两者都是通过运行以下命令构建和执行的:
make test
除了下面列出的组件外,此存储库中的 Argon2 代码的版权归 (c) 2015 Daniel Dinu、Dmitry Khovratovich(主要作者)、Jean-Philippe Aumasson 和 Samuel Neves 所有,并根据CC0 许可证和Apache 2.0 许可证双重许可。 有关更多信息,请参见 LICENSE 文件。
src/encoding.c
中的字符串编码例程的版权归 (c) 2015 Thomas Pornin 所有,并根据CC0 许可证获得许可。
src/blake2/
中的 BLAKE2 代码的版权归 (c) Samuel Neves, 2013-2015 所有,并根据CC0 许可证获得许可。
因此,所有许可证都与 GPL 兼容。