示例说明
argon2id 是内存困难的慢哈希,GPU 与 ASIC 都难以加速爆破。Params 的三要素——内存 19 MiB、迭代两次、单线程——是交互式登录的常见平衡点。SaltString 由操作系统随机源生成并直接编码进密文字符串:存储格式自带算法版本与全部参数,未来升级成本时旧哈希仍可按自身参数验证。
技术要点
- 成本参数随密文编码持久化
- 验证失败与格式损坏统一返回假
- 随机盐保证同密码不同密文
代码解读
tuned_argon2 构造参数化实例;hash_password 每次生成新盐后哈希,输出是自描述的 PHC 格式字符串;verify_password 先解析存储哈希再恒时验证——解析失败直接拒绝不 panic。main 验证三件事:错误口令拒绝、正确口令通过、两次哈希因盐不同而不相等。
使用建议
参数提升前压测登录吞吐。存储侧可以并行保存哈希与参数版本,登录时透明升级重哈希。口令策略配合最小长度而非复杂度规则。
代码示例
use argon2::password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString};
use argon2::{Algorithm, Argon2, Params, Version};
use rand_core::OsRng;
fn tuned_argon2() -> Argon2<'static> {
let params = Params::new(
19_456, // 19 MiB 内存成本
2, // 时间成本(迭代次数)
1, // 并行度
None,
)
.expect("valid parameters");
Argon2::new(Algorithm::Argon2id, Version::V0x13, params)
}
fn hash_password(plain: &str) -> Result<String, String> {
let salt = SaltString::generate(&mut OsRng);
tuned_argon2()
.hash_password(plain.as_bytes(), &salt)
.map(|hash| hash.to_string())
.map_err(|err| format!("hash failed: {err}"))
}
fn verify_password(plain: &str, stored: &str) -> bool {
let parsed = match PasswordHash::new(stored) {
Ok(hash) => hash,
Err(_) => return false, // 存储格式损坏视为验证失败
};
tuned_argon2()
.verify_password(plain.as_bytes(), &parsed)
.is_ok()
}
fn main() {
let stored = hash_password("correct horse battery staple").unwrap();
println!("format prefix: {}", &stored[..stored.find('$').unwrap()]);
println!("wrong rejected: {}", !verify_password("wrong", &stored));
println!("right accepted: {}", verify_password("correct horse battery staple", &stored));
// 不同盐产出不同密文:同密码两次哈希不相等
let again = hash_password("correct horse battery staple").unwrap();
println!("salts differ: {}", stored != again);
}