1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
use argon2::{Algorithm, Version};
use once_cell::sync::OnceCell;
pub(crate) static GLOBAL_CONFIG: OnceCell<tokio::sync::RwLock<Config>> = OnceCell::new();
/// Set the global config.
pub async fn set_config(config: Config<'static>) {
match GLOBAL_CONFIG.get() {
Some(cfg) => {
let mut cfg = cfg.write().await;
*cfg = config;
}
None => assert!(GLOBAL_CONFIG.set(tokio::sync::RwLock::new(config)).is_ok()),
}
}
/// Configuration for the `argon2` algorithm.
pub struct Config<'k> {
/// Set the hashing algorithm in use.
///
/// According to the [latest (as of 5/18) Argon2 RFC](https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03) ...
/// "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
pub algorithm: Algorithm,
/// Set the version of `argon2` to use.
pub version: Version,
/// The secret key to use.
///
/// This is **strongly** recommended in production.
pub secret_key: Option<&'k [u8]>,
/// The memory cost of the algorithm in kilobytes.
///
/// Argon2 has a notion of "memory size" or "memory cost" (in kilobytes). All else equal
/// and generally speaking, the greater the memory cost, the longer it takes to perform
/// the hash and the more secure the resulting hash. More memory cost basically means
/// more memory used. This and "iterations" are, generally speaking, the two parameters
/// to adjust in order to increase or decrease the security of your hash. If you're going
/// to use this crate 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).
pub memory_cost: u32,
/// The number of iterations to use.
///
/// 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 cost" are the two primary parameters to adjust in order
/// to increase or decrease the security of your hash. If you're going to use this
/// crate in production, you should probably tweak this parameter (and the memory cost
/// 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).
pub iterations: u32,
/// The parallelism of the algorithm.
///
/// 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, the hashing algorithm
/// will perform 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.
pub parallelism: u32,
/// The output length of the algorithm in bytes.
pub output_length: Option<usize>,
}
impl<'k> Default for Config<'k> {
/// Create a new default config. This is good for basic purposes,
/// but **SHOULD NOT** be used in a production environment.
///
/// * Algorithm: Argon2id
/// * Version: 19 (0x13)
/// * Secret key: None,
/// * Memory size: 4,096 kilobytes
/// * Iterations: 3
/// * Parallelism: 1
/// * Output length: Some(32)
#[inline]
fn default() -> Self {
Self {
algorithm: Algorithm::Argon2id,
version: Version::V0x13,
secret_key: None,
memory_cost: 512,
iterations: 3,
parallelism: 1,
output_length: Some(32),
}
}
}
impl<'k> Config<'k> {
/// Create a new config. This is an insecure config and **should not be used in production**!
#[inline]
pub fn new_insecure() -> Self {
Self::default()
}
/// Create a new config. This config is somewhat more secure than `new_insecure()` but a secret key should still be set with the `set_secret_key()` function.
#[inline]
pub fn new() -> Self {
Self {
memory_cost: 8192,
iterations: 200,
parallelism: num_cpus::get_physical() as u32,
..Self::default()
}
}
}
impl<'k> Config<'k> {
/// Set the hashing algorithm in use.
///
/// The default (Argon2id) should be fine for most uses.
#[inline]
pub fn set_algorithm(&mut self, algorithm: Algorithm) -> &mut Self {
self.algorithm = algorithm;
self
}
/// Set the version of `argon2` to use.
///
/// The default (0x13 or v19) should be fine for most uses.
#[inline]
pub fn set_version(&mut self, version: Version) -> &mut Self {
self.version = version;
self
}
/// Set the secret key to use. This is **strongly** recommended in a production environment.
#[inline]
pub fn set_secret_key(&mut self, secret_key: Option<&'k [u8]>) -> &mut Self {
self.secret_key = secret_key;
self
}
/// Set the memory cost in kilobytes.
///
/// Default: 512 for insecure, and 4,096 for secure.
#[inline]
pub fn set_memory_cost(&mut self, memory_cost: u32) -> &mut Self {
self.memory_cost = memory_cost;
self
}
/// Set the number of iterations to use.
///
/// Default: 3 for insecure, and 200 for secure.
#[inline]
pub fn set_iterations(&mut self, iterations: u32) -> &mut Self {
self.iterations = iterations;
self
}
/// Set the parallelism of the algorithm.
///
/// Default: 1 for insecure, and the number of physical CPU cores on the host for secure.
#[inline]
pub fn set_parallelism(&mut self, parallelism: u32) -> &mut Self {
self.parallelism = parallelism;
self
}
/// Set the output length of the algorithm in bytes.
///
/// Default: 32
#[inline]
pub fn set_output_length(&mut self, output_length: Option<usize>) -> &mut Self {
self.output_length = output_length;
self
}
}
