#password-hashing #argon2 #secret-key #password #password-hash #hash #security

bin+lib argonautica

针对 Rust 的惯用 Argon2 密码哈希

7 个版本

使用旧版 Rust 2015

0.2.0 2019年3月5日
0.1.5 2018年6月28日

#1839加密学

Download history 620/week @ 2024-03-14 492/week @ 2024-03-21 400/week @ 2024-03-28 497/week @ 2024-04-04 757/week @ 2024-04-11 593/week @ 2024-04-18 484/week @ 2024-04-25 470/week @ 2024-05-02 391/week @ 2024-05-09 671/week @ 2024-05-16 534/week @ 2024-05-23 525/week @ 2024-05-30 449/week @ 2024-06-06 561/week @ 2024-06-13 412/week @ 2024-06-20 512/week @ 2024-06-27

1,981 每月下载量
用于 8 个crate (6 个直接使用)

MIT/Apache 许可

2MB
13K SLoC

VB6 5.5K SLoC Rust 2.5K SLoC // 0.0% comments C 2.5K SLoC // 0.2% comments Visual Studio Project 2.5K SLoC Visual Studio Solution 160 SLoC PowerShell 70 SLoC // 0.0% comments Shell 55 SLoC // 0.1% comments Pan 1 SLoC

argonautica-rs

Build Status Crates.io Documentation Github.com License

概述

argonautica 是一个 Rust 包,用于使用加密安全的 Argon2 哈希算法 对密码进行哈希处理。

Argon2 在 2015 年赢得了 密码哈希竞赛,这是一个为期数年的项目,旨在寻找 bcryptscrypt 和其他常用加密安全的哈希算法的继任者。

argonautica 包被设计成

  • 易于使用,
  • 拥有强大、适合初学者的文档,并且
  • 尽可能遵循 Rust API 指南

argonautica 考虑到的简单用例是:对网站数据库中的密码进行哈希处理。但即便如此,它也“功能齐全”,意味着你可以使用 argonautica* 做到任何使用 Argon2 的标准 C 实现所能做到的事情。

* 事实上,argonautica 有一个连标准 C 实现都没有的功能,即使用密钥进行密码哈希(C 实现实现了这个功能,但没有公开暴露)

哈希处理

使用 argonautica 对密码进行哈希处理很简单。只需实例化一个默认的 Hasher,向它提供密码和密钥,然后调用 hash 方法。

extern crate argonautica;

use argonautica::Hasher;

fn main() {
    let mut hasher = Hasher::default();
    let hash = hasher
        .with_password("P@ssw0rd")
        .with_secret_key("\
            secret key that you should really store in a .env file \
            instead of in code, but this is just an example\
        ")
        .hash()
        .unwrap();

    println!("{}", &hash);
    // 👆 prints a hash, which will be random since the default Hasher uses a random salt
}

验证

与散列值比对密码同样简单。只需实例化一个默认的 Verifier,向其提供密码和要比较的散列值,以及创建散列值时使用的密钥,然后调用 verify 方法。

extern crate argonautica;

use argonautica::Verifier;

fn main() {
    let mut verifier = Verifier::default();
    let is_valid = verifier
        .with_hash("
            $argon2id$v=19$m=4096,t=192,p=4$\
            o2y5PU86Vt+sr93N7YUGgC7AMpTKpTQCk4tNGUPZMY4$\
            yzP/ukZRPIbZg6PvgnUUobUMbApfF9RH6NagL9L4Xr4\
        ")
        .with_password("P@ssw0rd")
        .with_secret_key("\
            secret key that you should really store in a .env file \
            instead of in code, but this is just an example\
        ")
        .verify()
        .unwrap();

    assert!(is_valid);
}

替代方案

如果你不喜欢 argonautica,还有其他 Rust crate 可以帮你完成 Argon2 散列,包括 argon2rsrust-argon2。如果你对使用不同算法进行密码散列感兴趣,rust-bcrypt 可能值得一试。

值得一提的是,除了 API 差异之外,argonautica 有三个关键特性是其他 crate 目前所不具备的

  • 能够使用 SIMD 指令(甚至在稳定版本中),这可以显著提高散列速度
    • 例如,在默认设置下,使用 SIMD 指令的 argonautica 比其他 crate 在开发者的 2014 年初 MacBook 上快 超过两倍,该笔记本可以通过 SIMD 指令 通过 AVX2 使用 SIMD 指令
    • 注意:SIMD 指令针对你的 CPU 而定;因此,如果你正在为除你自己的机器以外的机器编译,请不要启用 SIMD 功能
  • 使用密钥散列密码的能力,这一点连 C 实现 都没有公开
  • 最新的 Argon2 变体:Argon2id

配置

HasherVerifier 的默认配置是为了满足在网站数据库中存储密码的一般用例而选择的安全合理的,但如果你有其他原因想要使用 argonautica 或者你不同意选择的默认设置,自定义 argonautica 以满足你的需求应该和默认设置一样容易和直观。

以下是一个示例,展示了如何使用 Hasher 的自定义配置选项。它为每个选项提供了颜色。

extern crate argonautica;
extern crate futures_cpupool;

use argonautica::Hasher;
use argonautica::config::{Backend, Variant, Version};
use futures_cpupool::CpuPool;

fn main() {
    let mut hasher = Hasher::default();
    hasher
        .configure_backend(Backend::C) // Default is `Backend::C`
        // 👆 argonautica was designed to support multiple backends (meaning multiple
        // implementations of the underlying Argon2 algorithm). Currently only the C backend
        // is supported, which uses the cannonical Argon2 library written in C to actually
        // do the work. In the future hopefully a Rust backend will also be supported, but,
        // for the moment, you must use `Backend::C`, which is the default. Using
        // `Backend::Rust` will result in an error (again, for the moment).
        .configure_cpu_pool(CpuPool::new(2))
        // 👆 There are two non-blocking methods on `Hasher` that perform computation on
        // a separate thread and return a `Future` instead of a `Result` (`hash_non_blocking`
        // and `hash_raw_non_blocking`). These methods allow argonautica to play nicely with
        // futures-heavy code, but need a `CpuPool` in order to work. The blocking
        // methods `hash` and `hash_raw` do not use a 'CpuPool'; so if you are using only
        // these blocking methods you can ignore this configuration entirely. If, however,
        // you are using the non-blocking methods and would like to provide your own `CpuPool`
        // instead of using the default, which is a lazily created `CpuPool` with the number
        // of threads equal to the number of logical cores on your machine, you can
        // configure your `Hasher` with a custom `CpuPool` using this method. This
        // might be useful if, for example, you are writing code in an environment which
        // makes heavy use of futures, the code you are writing uses both a `Hasher` and
        // a `Verifier`, and you would like both of them to share the same underlying
        // `CpuPool`.
        .configure_hash_len(16) // Default is `32`
        // 👆 The hash length in bytes is configurable. The default is 32. This is probably
        // a good number to use. 16 is also probably fine. You probably shouldn't go below 16
        .configure_iterations(192) // Default is `192`
        // 👆 Argon2 has a notion of "iterations" or "time cost". All else equal and generally
        // speaking, the greater the number of iterations, the longer it takes to perform the
        // hash and the more secure the resulting hash. More iterations basically means more
        // CPU load. This and "memory size" (see below) are the two primary parameters to
        // adjust in order to increase or decrease the security of your hash. The default is
        // 192 iterations, which was chosen because, along with the default memory size of
        // 4096, this leads to a hashing time of approximately 300 milliseconds on the
        // early-2014 Macbook Air that is the developer's machine. If you're going to use
        // argonautica in production, you should probably tweak this parameter (and the memory
        // size parameter) in order to increase the time it takes to hash to the maximum you
        // can reasonably allow for your use-case (e.g. to probably about 300-500 milliseconds
        // for the use-case of hashing user passwords for a website)
        .configure_lanes(2) // Default is number of logical cores on your machine
        // 👆 Argon2 can break up its work into one or more "lanes" during some parts of
        // the hashing algorithm. If you configure it with multiple lanes and you also
        // use multiple threads (see below) the hashing algorithm will performed its
        // work in parallel in some parts, potentially speeding up the time it takes to
        // produce a hash without diminishing the security of the result. By default,
        // the number of lanes is set to the number of logical cores on your machine
        .configure_memory_size(4096) // Default is `4096`
        // 👆 Argon2 has a notion of "memory size" or "memory cost" (in kibibytes). All else
        // equal and generally speaking, the greater the memory size, the longer it takes to
        // perform the hash and the more secure the resulting hash. More memory size basically
        // means more memory used. This and "iterations" (see above) are, again, generally
        // speaking, the two parameters to adjust in order to increase or decrease the
        // security of your hash. The default is 4096 kibibytes, which was chosen because,
        // again, along with the default iterations of 192, this leads to a hashing time of
        // approximately 300 milliseconds on the early-2014 Macbook Air that is the
        // developer's machine. If you're going to use argonautica in production, you should
        // probably tweak this parameter (and the iterations parameter) in order to increase
        // the time it takes to hash to the maximum you can reasonably allow for your use-case
        // (e.g. to probably about 300-500 milliseconds for the use-case of hashing user
        // passwords for a website)
        .configure_password_clearing(false) // Default is `false`
        // 👆 It is possible to have the underlying bytes of the password you provided
        // to `Hasher` be erased after each call to `hash`, `hash_raw` or their non-blocking
        // equivalents. If you want this extra security feature, set this configuration
        // to `true` (the default is `false`). If you set this configuration to `true`,
        // you will be required to provide `Hasher` with a mutable password (e.g.
        // a `String`, a `Vec<u8>`, a `&mut str`, or a `&mut [u8]` instead of a
        // `&str` or a `&[u8]`)
        .configure_secret_key_clearing(false) // Default is `false`
        // 👆 It is also possible to have the underlying bytes of the secret key you provided
        // to `Hasher` be erased after each call to `hash`, `hash_raw` or their non-blocking
        // equivalents. If you want this extra security feature, set this configuration
        // to `true` (the default is `false`). If you set this configuration to `true`,
        // you will be required to provide `Hasher` with a mutable secret key (e.g.
        // a `String`, a `Vec<u8>`, a `&mut str`, or a `&mut [u8]` instead of a `&str`
        // or a `&[u8]`)
        .configure_threads(2) // Default is number of logical cores on your machine
        // 👆 If you have configured `Hasher` to use more than one lane (see above), you
        // can get the hashing algorithm to run in parallel during some parts of the
        // computation by setting the number of threads to be greater than one as well,
        // potentially speeding up the time it takes to produce a hash without diminishing
        // the security of the result. By default, the number of threads is set to the number
        // of logical cores on your machine. If you set the number of threads to a number
        // greater than the number of lanes, `Hasher` will automatically reduce the number
        // of threads to the number of lanes
        .configure_variant(Variant::Argon2id) // Default is `Variant::Argon2id`
        // 👆 Argon2 has three variants: Argon2d, Argon2i, and Argon2id. Here is how these
        // variants are explained in the RFC: "Argon2 has one primary variant: Argon2id,
        // and two supplementary variants: Argon2d and Argon2i. Argon2d uses data-dependent
        // memory access, which makes it suitable for ... applications with no threats from
        // side-channel timing attacks. Argon2i uses data-independent memory access, which
        // is preferred for password hashing and password-based key derivation. Argon2id
        // works as Argon2i for the first half of the first iteration over the memory, and
        // as Argon2d for the rest, thus providing both side-channel attack protection and
        // brute-force cost savings due to time-memory tradeoffs." If you do not know which
        // variant to use, use the default, which is Argon2id
        .configure_version(Version::_0x13) // Default is `Version::_0x13`
        // 👆 Argon2 has two versions: 0x10 and 0x13. The latest version is 0x13 (as of 5/18).
        // Unless you have a very specific reason not to, you should use the latest
        // version (0x13), which is also the default
        .opt_out_of_secret_key(true); // Default is `false`
        // 👆 As an extra security measure, if you want to hash without a secret key, which
        // is not recommended, you must explicitly declare that this is your intention
        // by calling this method and setting the `opt_out_of_secret_key` configuration to
        // `true` (by default, it is set to `false`); otherwise hashing will return an error
        // when you fail to provide a secret key

    let hash = hasher
        .with_password("P@ssw0rd")
        .with_salt("somesalt")
        .hash()
        .unwrap();
        // 👆 Note: We are able to hash witout a secret key because we explicitly
        // set `opt_out_of_secret_key` to `true` above

    assert_eq!(
        &hash,
        "$argon2id$v=19$m=4096,t=192,p=2$c29tZXNhbHQ$sw41ZsxebJmOJ6vSHe6BGQ",
    );
}

安装

argonautica 应该相对容易地包含在你的 Rust 项目中

  • 在你的代码中放置 extern crate argonautica;(通常在 lib.rsmain.rs 中)
  • 在你的 Cargo.toml[dependencies] 部分,放置 ...
    • ... 如果你正在为你的机器构建 ...
      • argonautica = { version = "0.2", features = ["simd"] },或者
      • argonautica= {version= "0.2",features= ["serde", "simd"] }
    • ... 如果你正在为其他机器构建 ...
      • argonautica = "0.2",或者
      • argonautica= {version= "0.2",features= ["serde"] }

尽管如此,argonautica 在构建过程中使用 ccbindgen 将 Argon2 的规范 C 实现 编译为静态库。这意味着您需要在机器上安装 C 编译器才能构建 argonautica。更具体地说,您需要

  • LLVM/Clang(版本 3.9 或更高版本)
    • Mac OS:brew install llvm,这需要 Homebrew
    • 基于 Debian 的 Linux:apt-get install clang llvm-dev libclang-dev
    • Arch Linux:pacman -S clang
    • Windows:在此下载预构建的二进制文件

argonautica 在稳定的 Rust 版本 1.26.0 或更高版本上运行。

许可证

argonautica 的许可证可以是以下之一

任选其一。

依赖关系

~3-14MB
~154K SLoC