chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

132
vendor/argon2/src/algorithm.rs vendored Normal file
View File

@@ -0,0 +1,132 @@
//! Argon2 algorithms (e.g. Argon2d, Argon2i, Argon2id).
use crate::{Error, Result};
use core::{
fmt::{self, Display},
str::FromStr,
};
#[cfg(feature = "password-hash")]
use password_hash::Ident;
/// Argon2d algorithm identifier
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub const ARGON2D_IDENT: Ident<'_> = Ident::new_unwrap("argon2d");
/// Argon2i algorithm identifier
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub const ARGON2I_IDENT: Ident<'_> = Ident::new_unwrap("argon2i");
/// Argon2id algorithm identifier
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub const ARGON2ID_IDENT: Ident<'_> = Ident::new_unwrap("argon2id");
/// Argon2 primitive type: variants of the algorithm.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Default, Ord)]
pub enum Algorithm {
/// Optimizes against GPU cracking attacks but vulnerable to side-channels.
///
/// Accesses the memory array in a password dependent order, reducing the
/// possibility of timememory tradeoff (TMTO) attacks.
Argon2d = 0,
/// Optimized to resist side-channel attacks.
///
/// Accesses the memory array in a password independent order, increasing the
/// possibility of time-memory tradeoff (TMTO) attacks.
Argon2i = 1,
/// Hybrid that mixes Argon2i and Argon2d passes (*default*).
///
/// Uses the Argon2i approach for the first half pass over memory and
/// Argon2d approach for subsequent passes. This effectively places it in
/// the "middle" between the other two: it doesn't provide as good
/// TMTO/GPU cracking resistance as Argon2d, nor as good of side-channel
/// resistance as Argon2i, but overall provides the most well-rounded
/// approach to both classes of attacks.
#[default]
Argon2id = 2,
}
impl Algorithm {
/// Parse an [`Algorithm`] from the provided string.
pub fn new(id: impl AsRef<str>) -> Result<Self> {
id.as_ref().parse()
}
/// Get the identifier string for this PBKDF2 [`Algorithm`].
pub const fn as_str(&self) -> &'static str {
match self {
Algorithm::Argon2d => "argon2d",
Algorithm::Argon2i => "argon2i",
Algorithm::Argon2id => "argon2id",
}
}
/// Get the [`Ident`] that corresponds to this Argon2 [`Algorithm`].
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub const fn ident(&self) -> Ident<'static> {
match self {
Algorithm::Argon2d => ARGON2D_IDENT,
Algorithm::Argon2i => ARGON2I_IDENT,
Algorithm::Argon2id => ARGON2ID_IDENT,
}
}
/// Serialize primitive type as little endian bytes
pub(crate) const fn to_le_bytes(self) -> [u8; 4] {
(self as u32).to_le_bytes()
}
}
impl AsRef<str> for Algorithm {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Display for Algorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for Algorithm {
type Err = Error;
fn from_str(s: &str) -> Result<Algorithm> {
match s {
"argon2d" => Ok(Algorithm::Argon2d),
"argon2i" => Ok(Algorithm::Argon2i),
"argon2id" => Ok(Algorithm::Argon2id),
_ => Err(Error::AlgorithmInvalid),
}
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl From<Algorithm> for Ident<'static> {
fn from(alg: Algorithm) -> Ident<'static> {
alg.ident()
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl<'a> TryFrom<Ident<'a>> for Algorithm {
type Error = password_hash::Error;
fn try_from(ident: Ident<'a>) -> password_hash::Result<Algorithm> {
match ident {
ARGON2D_IDENT => Ok(Algorithm::Argon2d),
ARGON2I_IDENT => Ok(Algorithm::Argon2i),
ARGON2ID_IDENT => Ok(Algorithm::Argon2id),
_ => Err(password_hash::Error::Algorithm),
}
}
}

79
vendor/argon2/src/blake2b_long.rs vendored Normal file
View File

@@ -0,0 +1,79 @@
//! The variable length hash function used in the Argon2 algorithm.
use crate::{Error, Result};
use blake2::{
digest::{self, Digest, VariableOutput},
Blake2b512, Blake2bVar,
};
use core::convert::TryFrom;
pub fn blake2b_long(inputs: &[&[u8]], out: &mut [u8]) -> Result<()> {
if out.is_empty() {
return Err(Error::OutputTooShort);
}
let len_bytes = u32::try_from(out.len())
.map(|v| v.to_le_bytes())
.map_err(|_| Error::OutputTooLong)?;
// Use blake2b directly if the output is small enough.
if out.len() <= Blake2b512::output_size() {
let mut digest = Blake2bVar::new(out.len()).map_err(|_| Error::OutputTooLong)?;
// Conflicting method name from `Digest` and `Update` traits
digest::Update::update(&mut digest, &len_bytes);
for input in inputs {
digest::Update::update(&mut digest, input);
}
digest
.finalize_variable(out)
.map_err(|_| Error::OutputTooLong)?;
return Ok(());
}
// Calculate longer hashes by first calculating a full 64 byte hash
let half_hash_len = Blake2b512::output_size() / 2;
let mut digest = Blake2b512::new();
digest.update(len_bytes);
for input in inputs {
digest.update(input);
}
let mut last_output = digest.finalize();
// Then we write the first 32 bytes of this hash to the output
out[..half_hash_len].copy_from_slice(&last_output[..half_hash_len]);
// Next, we write a number of 32 byte blocks to the output.
// Each block is the first 32 bytes of the hash of the last block.
// The very last block of the output is excluded, and has a variable
// length in range [1, 32].
let mut counter = 0;
let out_len = out.len();
for chunk in out[half_hash_len..]
.chunks_exact_mut(half_hash_len)
.take_while(|_| {
counter += half_hash_len;
out_len - counter > 64
})
{
last_output = Blake2b512::digest(last_output);
chunk.copy_from_slice(&last_output[..half_hash_len]);
}
// Calculate the last block with VarBlake2b.
let last_block_size = out.len() - counter;
let mut digest = Blake2bVar::new(last_block_size).map_err(|_| Error::OutputTooLong)?;
digest::Update::update(&mut digest, &last_output);
digest
.finalize_variable(&mut out[counter..])
.expect("invalid Blake2bVar out length");
Ok(())
}

156
vendor/argon2/src/block.rs vendored Normal file
View File

@@ -0,0 +1,156 @@
//! Argon2 memory block functions
use core::{
convert::{AsMut, AsRef},
num::Wrapping,
ops::{BitXor, BitXorAssign},
slice,
};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
const TRUNC: u64 = u32::MAX as u64;
#[rustfmt::skip]
macro_rules! permute_step {
($a:expr, $b:expr, $c:expr, $d:expr) => {
$a = (Wrapping($a) + Wrapping($b) + (Wrapping(2) * Wrapping(($a & TRUNC) * ($b & TRUNC)))).0;
$d = ($d ^ $a).rotate_right(32);
$c = (Wrapping($c) + Wrapping($d) + (Wrapping(2) * Wrapping(($c & TRUNC) * ($d & TRUNC)))).0;
$b = ($b ^ $c).rotate_right(24);
$a = (Wrapping($a) + Wrapping($b) + (Wrapping(2) * Wrapping(($a & TRUNC) * ($b & TRUNC)))).0;
$d = ($d ^ $a).rotate_right(16);
$c = (Wrapping($c) + Wrapping($d) + (Wrapping(2) * Wrapping(($c & TRUNC) * ($d & TRUNC)))).0;
$b = ($b ^ $c).rotate_right(63);
};
}
macro_rules! permute {
(
$v0:expr, $v1:expr, $v2:expr, $v3:expr,
$v4:expr, $v5:expr, $v6:expr, $v7:expr,
$v8:expr, $v9:expr, $v10:expr, $v11:expr,
$v12:expr, $v13:expr, $v14:expr, $v15:expr,
) => {
permute_step!($v0, $v4, $v8, $v12);
permute_step!($v1, $v5, $v9, $v13);
permute_step!($v2, $v6, $v10, $v14);
permute_step!($v3, $v7, $v11, $v15);
permute_step!($v0, $v5, $v10, $v15);
permute_step!($v1, $v6, $v11, $v12);
permute_step!($v2, $v7, $v8, $v13);
permute_step!($v3, $v4, $v9, $v14);
};
}
/// Structure for the (1 KiB) memory block implemented as 128 64-bit words.
#[derive(Copy, Clone, Debug)]
#[repr(align(64))]
pub struct Block([u64; Self::SIZE / 8]);
impl Block {
/// Memory block size in bytes
pub const SIZE: usize = 1024;
/// Returns a Block initialized with zeros.
pub const fn new() -> Self {
Self([0u64; Self::SIZE / 8])
}
/// Load a block from a block-sized byte slice
#[inline(always)]
pub(crate) fn load(&mut self, input: &[u8; Block::SIZE]) {
for (i, chunk) in input.chunks(8).enumerate() {
self.0[i] = u64::from_le_bytes(chunk.try_into().expect("should be 8 bytes"));
}
}
/// Iterate over the `u64` values contained in this block
#[inline(always)]
pub(crate) fn iter(&self) -> slice::Iter<'_, u64> {
self.0.iter()
}
/// NOTE: do not call this directly. It should only be called via
/// `Argon2::compress`.
#[inline(always)]
pub(crate) fn compress(rhs: &Self, lhs: &Self) -> Self {
let r = *rhs ^ lhs;
// Apply permutations rowwise
let mut q = r;
for chunk in q.0.chunks_exact_mut(16) {
#[rustfmt::skip]
permute!(
chunk[0], chunk[1], chunk[2], chunk[3],
chunk[4], chunk[5], chunk[6], chunk[7],
chunk[8], chunk[9], chunk[10], chunk[11],
chunk[12], chunk[13], chunk[14], chunk[15],
);
}
// Apply permutations columnwise
for i in 0..8 {
let b = i * 2;
#[rustfmt::skip]
permute!(
q.0[b], q.0[b + 1],
q.0[b + 16], q.0[b + 17],
q.0[b + 32], q.0[b + 33],
q.0[b + 48], q.0[b + 49],
q.0[b + 64], q.0[b + 65],
q.0[b + 80], q.0[b + 81],
q.0[b + 96], q.0[b + 97],
q.0[b + 112], q.0[b + 113],
);
}
q ^= &r;
q
}
}
impl Default for Block {
fn default() -> Self {
Self([0u64; Self::SIZE / 8])
}
}
impl AsRef<[u64]> for Block {
fn as_ref(&self) -> &[u64] {
&self.0
}
}
impl AsMut<[u64]> for Block {
fn as_mut(&mut self) -> &mut [u64] {
&mut self.0
}
}
impl BitXor<&Block> for Block {
type Output = Block;
fn bitxor(mut self, rhs: &Block) -> Self::Output {
self ^= rhs;
self
}
}
impl BitXorAssign<&Block> for Block {
fn bitxor_assign(&mut self, rhs: &Block) {
for (dst, src) in self.0.iter_mut().zip(rhs.0.iter()) {
*dst ^= src;
}
}
}
#[cfg(feature = "zeroize")]
impl Zeroize for Block {
fn zeroize(&mut self) {
self.0.zeroize();
}
}

131
vendor/argon2/src/error.rs vendored Normal file
View File

@@ -0,0 +1,131 @@
//! Error type
use core::fmt;
#[cfg(feature = "password-hash")]
use {crate::Params, core::cmp::Ordering, password_hash::errors::InvalidValue};
/// Result with argon2's [`Error`] type.
pub type Result<T> = core::result::Result<T, Error>;
/// Error type.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
/// Associated data is too long.
AdTooLong,
/// Algorithm identifier invalid.
AlgorithmInvalid,
/// "B64" encoding is invalid.
B64Encoding(base64ct::Error),
/// Key ID is too long.
KeyIdTooLong,
/// Memory cost is too small.
MemoryTooLittle,
/// Memory cost is too large.
MemoryTooMuch,
/// Output is too short.
OutputTooShort,
/// Output is too long.
OutputTooLong,
/// Password is too long.
PwdTooLong,
/// Salt is too short.
SaltTooShort,
/// Salt is too long.
SaltTooLong,
/// Secret is too long.
SecretTooLong,
/// Not enough threads.
ThreadsTooFew,
/// Too many threads.
ThreadsTooMany,
/// Time cost is too small.
TimeTooSmall,
/// Invalid version
VersionInvalid,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Error::AdTooLong => "associated data is too long",
Error::AlgorithmInvalid => "algorithm identifier invalid",
Error::B64Encoding(inner) => return write!(f, "B64 encoding invalid: {inner}"),
Error::KeyIdTooLong => "key ID is too long",
Error::MemoryTooLittle => "memory cost is too small",
Error::MemoryTooMuch => "memory cost is too large",
Error::OutputTooShort => "output is too short",
Error::OutputTooLong => "output is too long",
Error::PwdTooLong => "password is too long",
Error::SaltTooShort => "salt is too short",
Error::SaltTooLong => "salt is too long",
Error::SecretTooLong => "secret is too long",
Error::ThreadsTooFew => "not enough threads",
Error::ThreadsTooMany => "too many threads",
Error::TimeTooSmall => "time cost is too small",
Error::VersionInvalid => "invalid version",
})
}
}
impl From<base64ct::Error> for Error {
fn from(err: base64ct::Error) -> Error {
Error::B64Encoding(err)
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl From<Error> for password_hash::Error {
fn from(err: Error) -> password_hash::Error {
match err {
Error::AdTooLong => InvalidValue::TooLong.param_error(),
Error::AlgorithmInvalid => password_hash::Error::Algorithm,
Error::B64Encoding(inner) => password_hash::Error::B64Encoding(inner),
Error::KeyIdTooLong => InvalidValue::TooLong.param_error(),
Error::MemoryTooLittle => InvalidValue::TooShort.param_error(),
Error::MemoryTooMuch => InvalidValue::TooLong.param_error(),
Error::PwdTooLong => password_hash::Error::Password,
Error::OutputTooShort => password_hash::Error::OutputSize {
provided: Ordering::Less,
expected: Params::MIN_OUTPUT_LEN,
},
Error::OutputTooLong => password_hash::Error::OutputSize {
provided: Ordering::Greater,
expected: Params::MAX_OUTPUT_LEN,
},
Error::SaltTooShort => InvalidValue::TooShort.salt_error(),
Error::SaltTooLong => InvalidValue::TooLong.salt_error(),
Error::SecretTooLong => InvalidValue::TooLong.param_error(),
Error::ThreadsTooFew => InvalidValue::TooShort.param_error(),
Error::ThreadsTooMany => InvalidValue::TooLong.param_error(),
Error::TimeTooSmall => InvalidValue::TooShort.param_error(),
Error::VersionInvalid => password_hash::Error::Version,
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::B64Encoding(err) => Some(err),
_ => None,
}
}
}

696
vendor/argon2/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,696 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
)]
#![warn(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::checked_conversions,
clippy::implicit_saturating_sub,
clippy::panic,
clippy::panic_in_result_fn,
clippy::unwrap_used,
missing_docs,
rust_2018_idioms,
unused_lifetimes,
unused_qualifications
)]
//! ## Usage
//!
//! ### Password Hashing
//!
//! This API hashes a password to a "PHC string" suitable for the purposes of
//! password-based authentication. Do not use this API to derive cryptographic
//! keys: see the "key derivation" usage example below.
//!
#![cfg_attr(feature = "std", doc = "```")]
#![cfg_attr(not(feature = "std"), doc = "```ignore")]
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use argon2::{
//! password_hash::{
//! rand_core::OsRng,
//! PasswordHash, PasswordHasher, PasswordVerifier, SaltString
//! },
//! Argon2
//! };
//!
//! let password = b"hunter42"; // Bad password; don't actually use!
//! let salt = SaltString::generate(&mut OsRng);
//!
//! // Argon2 with default params (Argon2id v19)
//! let argon2 = Argon2::default();
//!
//! // Hash password to PHC string ($argon2id$v=19$...)
//! let password_hash = argon2.hash_password(password, &salt)?.to_string();
//!
//! // Verify password against PHC string.
//! //
//! // NOTE: hash params from `parsed_hash` are used instead of what is configured in the
//! // `Argon2` instance.
//! let parsed_hash = PasswordHash::new(&password_hash)?;
//! assert!(Argon2::default().verify_password(password, &parsed_hash).is_ok());
//! # Ok(())
//! # }
//! ```
//!
//! ### Key Derivation
//!
//! This API is useful for transforming a password into cryptographic keys for
//! e.g. password-based encryption.
//!
#![cfg_attr(feature = "std", doc = "```")]
#![cfg_attr(not(feature = "std"), doc = "```ignore")]
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use argon2::Argon2;
//!
//! let password = b"hunter42"; // Bad password; don't actually use!
//! let salt = b"example salt"; // Salt should be unique per password
//!
//! let mut output_key_material = [0u8; 32]; // Can be any desired size
//! Argon2::default().hash_password_into(password, salt, &mut output_key_material)?;
//! # Ok(())
//! # }
//! ```
// Call sites which cast `u32` to `usize` and are annotated with
// allow(clippy::cast_possible_truncation) need this check to avoid truncation.
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
compile_error!("this crate builds on 32-bit and 64-bit platforms only");
#[cfg(feature = "alloc")]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
mod algorithm;
mod blake2b_long;
mod block;
mod error;
mod params;
mod version;
pub use crate::{
algorithm::Algorithm,
block::Block,
error::{Error, Result},
params::{AssociatedData, KeyId, Params, ParamsBuilder},
version::Version,
};
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub use {
crate::algorithm::{ARGON2D_IDENT, ARGON2ID_IDENT, ARGON2I_IDENT},
password_hash::{self, PasswordHash, PasswordHasher, PasswordVerifier},
};
use crate::blake2b_long::blake2b_long;
use blake2::{digest, Blake2b512, Digest};
use core::fmt;
#[cfg(all(feature = "alloc", feature = "password-hash"))]
use password_hash::{Decimal, Ident, ParamsString, Salt};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
/// Maximum password length in bytes.
pub const MAX_PWD_LEN: usize = 0xFFFFFFFF;
/// Minimum salt length in bytes.
pub const MIN_SALT_LEN: usize = 8;
/// Maximum salt length in bytes.
pub const MAX_SALT_LEN: usize = 0xFFFFFFFF;
/// Recommended salt length for password hashing in bytes.
pub const RECOMMENDED_SALT_LEN: usize = 16;
/// Maximum secret key length in bytes.
pub const MAX_SECRET_LEN: usize = 0xFFFFFFFF;
/// Number of synchronization points between lanes per pass
pub(crate) const SYNC_POINTS: usize = 4;
/// To generate reference block positions
const ADDRESSES_IN_BLOCK: usize = 128;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
cpufeatures::new!(avx2_cpuid, "avx2");
/// Argon2 context.
///
/// This is the primary type of this crate's API, and contains the following:
///
/// - Argon2 [`Algorithm`] variant to be used
/// - Argon2 [`Version`] to be used
/// - Default set of [`Params`] to be used
/// - (Optional) Secret key a.k.a. "pepper" to be used
#[derive(Clone)]
pub struct Argon2<'key> {
/// Algorithm to use
algorithm: Algorithm,
/// Version number
version: Version,
/// Algorithm parameters
params: Params,
/// Key array
secret: Option<&'key [u8]>,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
cpu_feat_avx2: avx2_cpuid::InitToken,
}
impl Default for Argon2<'_> {
fn default() -> Self {
Self::new(Algorithm::default(), Version::default(), Params::default())
}
}
impl fmt::Debug for Argon2<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("Argon2")
.field("algorithm", &self.algorithm)
.field("version", &self.version)
.field("params", &self.params)
.finish_non_exhaustive()
}
}
impl<'key> Argon2<'key> {
/// Create a new Argon2 context.
pub fn new(algorithm: Algorithm, version: Version, params: Params) -> Self {
Self {
algorithm,
version,
params,
secret: None,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
cpu_feat_avx2: avx2_cpuid::init(),
}
}
/// Create a new Argon2 context.
pub fn new_with_secret(
secret: &'key [u8],
algorithm: Algorithm,
version: Version,
params: Params,
) -> Result<Self> {
if MAX_SECRET_LEN < secret.len() {
return Err(Error::SecretTooLong);
}
Ok(Self {
algorithm,
version,
params,
secret: Some(secret),
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
cpu_feat_avx2: avx2_cpuid::init(),
})
}
/// Hash a password and associated parameters into the provided output buffer.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn hash_password_into(&self, pwd: &[u8], salt: &[u8], out: &mut [u8]) -> Result<()> {
let mut blocks = vec![Block::default(); self.params.block_count()];
self.hash_password_into_with_memory(pwd, salt, out, &mut blocks)
}
/// Hash a password and associated parameters into the provided output buffer.
///
/// This method takes an explicit `memory_blocks` parameter which allows
/// the caller to provide the backing storage for the algorithm's state:
///
/// - Users with the `alloc` feature enabled can use [`Argon2::hash_password_into`]
/// to have it allocated for them.
/// - `no_std` users on "heapless" targets can use an array of the [`Block`] type
/// to stack allocate this buffer.
pub fn hash_password_into_with_memory(
&self,
pwd: &[u8],
salt: &[u8],
out: &mut [u8],
mut memory_blocks: impl AsMut<[Block]>,
) -> Result<()> {
// Validate output length
if out.len() < self.params.output_len().unwrap_or(Params::MIN_OUTPUT_LEN) {
return Err(Error::OutputTooShort);
}
if out.len() > self.params.output_len().unwrap_or(Params::MAX_OUTPUT_LEN) {
return Err(Error::OutputTooLong);
}
Self::verify_inputs(pwd, salt)?;
// Hashing all inputs
let initial_hash = self.initial_hash(pwd, salt, out);
self.fill_blocks(memory_blocks.as_mut(), initial_hash)?;
self.finalize(memory_blocks.as_mut(), out)
}
/// Use a password and associated parameters only to fill the given memory blocks.
///
/// This method omits the calculation of a hash and can be used when only the
/// filled memory is required. It is not necessary to call this method
/// before calling any of the hashing functions.
pub fn fill_memory(
&self,
pwd: &[u8],
salt: &[u8],
mut memory_blocks: impl AsMut<[Block]>,
) -> Result<()> {
Self::verify_inputs(pwd, salt)?;
let initial_hash = self.initial_hash(pwd, salt, &[]);
self.fill_blocks(memory_blocks.as_mut(), initial_hash)
}
#[allow(clippy::cast_possible_truncation, unused_mut)]
fn fill_blocks(
&self,
memory_blocks: &mut [Block],
mut initial_hash: digest::Output<Blake2b512>,
) -> Result<()> {
let block_count = self.params.block_count();
let memory_blocks = memory_blocks
.get_mut(..block_count)
.ok_or(Error::MemoryTooLittle)?;
let segment_length = self.params.segment_length();
let iterations = self.params.t_cost() as usize;
let lane_length = self.params.lane_length();
let lanes = self.params.lanes();
// Initialize the first two blocks in each lane
for (l, lane) in memory_blocks.chunks_exact_mut(lane_length).enumerate() {
for (i, block) in lane[..2].iter_mut().enumerate() {
let i = i as u32;
let l = l as u32;
// Make the first and second block in each lane as G(H0||0||i) or
// G(H0||1||i)
let inputs = &[
initial_hash.as_ref(),
&i.to_le_bytes()[..],
&l.to_le_bytes()[..],
];
let mut hash = [0u8; Block::SIZE];
blake2b_long(inputs, &mut hash)?;
block.load(&hash);
}
}
#[cfg(feature = "zeroize")]
initial_hash.zeroize();
// Run passes on blocks
for pass in 0..iterations {
for slice in 0..SYNC_POINTS {
let data_independent_addressing = self.algorithm == Algorithm::Argon2i
|| (self.algorithm == Algorithm::Argon2id
&& pass == 0
&& slice < SYNC_POINTS / 2);
for lane in 0..lanes {
let mut address_block = Block::default();
let mut input_block = Block::default();
let zero_block = Block::default();
if data_independent_addressing {
input_block.as_mut()[..6].copy_from_slice(&[
pass as u64,
lane as u64,
slice as u64,
memory_blocks.len() as u64,
iterations as u64,
self.algorithm as u64,
]);
}
let first_block = if pass == 0 && slice == 0 {
if data_independent_addressing {
// Generate first set of addresses
self.update_address_block(
&mut address_block,
&mut input_block,
&zero_block,
);
}
// The first two blocks of each lane are already initialized
2
} else {
0
};
let mut cur_index = lane * lane_length + slice * segment_length + first_block;
let mut prev_index = if slice == 0 && first_block == 0 {
// Last block in current lane
cur_index + lane_length - 1
} else {
// Previous block
cur_index - 1
};
// Fill blocks in the segment
for block in first_block..segment_length {
// Extract entropy
let rand = if data_independent_addressing {
let addres_index = block % ADDRESSES_IN_BLOCK;
if addres_index == 0 {
self.update_address_block(
&mut address_block,
&mut input_block,
&zero_block,
);
}
address_block.as_ref()[addres_index]
} else {
memory_blocks[prev_index].as_ref()[0]
};
// Calculate source block index for compress function
let ref_lane = if pass == 0 && slice == 0 {
// Cannot reference other lanes yet
lane
} else {
(rand >> 32) as usize % lanes
};
let reference_area_size = if pass == 0 {
// First pass
if slice == 0 {
// First slice
block - 1 // all but the previous
} else if ref_lane == lane {
// The same lane => add current segment
slice * segment_length + block - 1
} else {
slice * segment_length - if block == 0 { 1 } else { 0 }
}
} else {
// Second pass
if ref_lane == lane {
lane_length - segment_length + block - 1
} else {
lane_length - segment_length - if block == 0 { 1 } else { 0 }
}
};
// 1.2.4. Mapping rand to 0..<reference_area_size-1> and produce
// relative position
let mut map = rand & 0xFFFFFFFF;
map = (map * map) >> 32;
let relative_position = reference_area_size
- 1
- ((reference_area_size as u64 * map) >> 32) as usize;
// 1.2.5 Computing starting position
let start_position = if pass != 0 && slice != SYNC_POINTS - 1 {
(slice + 1) * segment_length
} else {
0
};
let lane_index = (start_position + relative_position) % lane_length;
let ref_index = ref_lane * lane_length + lane_index;
// Calculate new block
let result =
self.compress(&memory_blocks[prev_index], &memory_blocks[ref_index]);
if self.version == Version::V0x10 || pass == 0 {
memory_blocks[cur_index] = result;
} else {
memory_blocks[cur_index] ^= &result;
};
prev_index = cur_index;
cur_index += 1;
}
}
}
}
Ok(())
}
fn compress(&self, rhs: &Block, lhs: &Block) -> Block {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
/// Enable AVX2 optimizations.
#[target_feature(enable = "avx2")]
unsafe fn compress_avx2(rhs: &Block, lhs: &Block) -> Block {
Block::compress(rhs, lhs)
}
if self.cpu_feat_avx2.get() {
return unsafe { compress_avx2(rhs, lhs) };
}
}
Block::compress(rhs, lhs)
}
/// Get default configured [`Params`].
pub const fn params(&self) -> &Params {
&self.params
}
fn finalize(&self, memory_blocks: &[Block], out: &mut [u8]) -> Result<()> {
let lane_length = self.params.lane_length();
let mut blockhash = memory_blocks[lane_length - 1];
// XOR the last blocks
for l in 1..self.params.lanes() {
let last_block_in_lane = l * lane_length + (lane_length - 1);
blockhash ^= &memory_blocks[last_block_in_lane];
}
// Hash the result
let mut blockhash_bytes = [0u8; Block::SIZE];
for (chunk, v) in blockhash_bytes.chunks_mut(8).zip(blockhash.iter()) {
chunk.copy_from_slice(&v.to_le_bytes())
}
blake2b_long(&[&blockhash_bytes], out)?;
#[cfg(feature = "zeroize")]
{
blockhash.zeroize();
blockhash_bytes.zeroize();
}
Ok(())
}
fn update_address_block(
&self,
address_block: &mut Block,
input_block: &mut Block,
zero_block: &Block,
) {
input_block.as_mut()[6] += 1;
*address_block = self.compress(zero_block, input_block);
*address_block = self.compress(zero_block, address_block);
}
/// Hashes all the inputs into `blockhash[PREHASH_DIGEST_LEN]`.
#[allow(clippy::cast_possible_truncation)]
fn initial_hash(&self, pwd: &[u8], salt: &[u8], out: &[u8]) -> digest::Output<Blake2b512> {
let mut digest = Blake2b512::new();
digest.update(self.params.p_cost().to_le_bytes());
digest.update((out.len() as u32).to_le_bytes());
digest.update(self.params.m_cost().to_le_bytes());
digest.update(self.params.t_cost().to_le_bytes());
digest.update(self.version.to_le_bytes());
digest.update(self.algorithm.to_le_bytes());
digest.update((pwd.len() as u32).to_le_bytes());
digest.update(pwd);
digest.update((salt.len() as u32).to_le_bytes());
digest.update(salt);
if let Some(secret) = &self.secret {
digest.update((secret.len() as u32).to_le_bytes());
digest.update(secret);
} else {
digest.update(0u32.to_le_bytes());
}
digest.update((self.params.data().len() as u32).to_le_bytes());
digest.update(self.params.data());
digest.finalize()
}
const fn verify_inputs(pwd: &[u8], salt: &[u8]) -> Result<()> {
if pwd.len() > MAX_PWD_LEN {
return Err(Error::PwdTooLong);
}
// Validate salt (required param)
if salt.len() < MIN_SALT_LEN {
return Err(Error::SaltTooShort);
}
if salt.len() > MAX_SALT_LEN {
return Err(Error::SaltTooLong);
}
Ok(())
}
}
#[cfg(all(feature = "alloc", feature = "password-hash"))]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl PasswordHasher for Argon2<'_> {
type Params = Params;
fn hash_password<'a>(
&self,
password: &[u8],
salt: impl Into<Salt<'a>>,
) -> password_hash::Result<PasswordHash<'a>> {
let salt = salt.into();
let mut salt_arr = [0u8; 64];
let salt_bytes = salt.decode_b64(&mut salt_arr)?;
let output_len = self
.params
.output_len()
.unwrap_or(Params::DEFAULT_OUTPUT_LEN);
let output = password_hash::Output::init_with(output_len, |out| {
Ok(self.hash_password_into(password, salt_bytes, out)?)
})?;
Ok(PasswordHash {
algorithm: self.algorithm.ident(),
version: Some(self.version.into()),
params: ParamsString::try_from(&self.params)?,
salt: Some(salt),
hash: Some(output),
})
}
fn hash_password_customized<'a>(
&self,
password: &[u8],
alg_id: Option<Ident<'a>>,
version: Option<Decimal>,
params: Params,
salt: impl Into<Salt<'a>>,
) -> password_hash::Result<PasswordHash<'a>> {
let algorithm = alg_id
.map(Algorithm::try_from)
.transpose()?
.unwrap_or_default();
let version = version
.map(Version::try_from)
.transpose()?
.unwrap_or_default();
let salt = salt.into();
Self {
secret: self.secret,
algorithm,
version,
params,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
cpu_feat_avx2: self.cpu_feat_avx2,
}
.hash_password(password, salt)
}
}
impl<'key> From<Params> for Argon2<'key> {
fn from(params: Params) -> Self {
Self::new(Algorithm::default(), Version::default(), params)
}
}
impl<'key> From<&Params> for Argon2<'key> {
fn from(params: &Params) -> Self {
Self::from(params.clone())
}
}
#[cfg(all(test, feature = "alloc", feature = "password-hash"))]
#[allow(clippy::unwrap_used)]
mod tests {
use crate::{Algorithm, Argon2, Params, PasswordHasher, Salt, Version};
/// Example password only: don't use this as a real password!!!
const EXAMPLE_PASSWORD: &[u8] = b"hunter42";
/// Example salt value. Don't use a static salt value!!!
const EXAMPLE_SALT: &str = "examplesaltvalue";
#[test]
fn decoded_salt_too_short() {
let argon2 = Argon2::default();
// Too short after decoding
let salt = Salt::from_b64("somesalt").unwrap();
let res =
argon2.hash_password_customized(EXAMPLE_PASSWORD, None, None, Params::default(), salt);
assert_eq!(
res,
Err(password_hash::Error::SaltInvalid(
password_hash::errors::InvalidValue::TooShort
))
);
}
#[test]
fn hash_simple_retains_configured_params() {
// Non-default but valid parameters
let t_cost = 4;
let m_cost = 2048;
let p_cost = 2;
let version = Version::V0x10;
let params = Params::new(m_cost, t_cost, p_cost, None).unwrap();
let hasher = Argon2::new(Algorithm::default(), version, params);
let salt = Salt::from_b64(EXAMPLE_SALT).unwrap();
let hash = hasher.hash_password(EXAMPLE_PASSWORD, salt).unwrap();
assert_eq!(hash.version.unwrap(), version.into());
for &(param, value) in &[("t", t_cost), ("m", m_cost), ("p", p_cost)] {
assert_eq!(
hash.params
.get(param)
.and_then(|p| p.decimal().ok())
.unwrap(),
value,
);
}
}
}

569
vendor/argon2/src/params.rs vendored Normal file
View File

@@ -0,0 +1,569 @@
//! Argon2 password hash parameters.
use crate::{Algorithm, Argon2, Error, Result, Version, SYNC_POINTS};
use base64ct::{Base64Unpadded as B64, Encoding};
use core::str::FromStr;
#[cfg(feature = "password-hash")]
use password_hash::{ParamsString, PasswordHash};
/// Argon2 password hash parameters.
///
/// These are parameters which can be encoded into a PHC hash string.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Params {
/// Memory size, expressed in kibibytes, between 8\*`p_cost` and (2^32)-1.
///
/// Value is an integer in decimal (1 to 10 digits).
m_cost: u32,
/// Number of iterations, between 1 and (2^32)-1.
///
/// Value is an integer in decimal (1 to 10 digits).
t_cost: u32,
/// Degree of parallelism, between 1 and (2^24)-1.
///
/// Value is an integer in decimal (1 to 8 digits).
p_cost: u32,
/// Key identifier.
keyid: KeyId,
/// Associated data.
data: AssociatedData,
/// Size of the output (in bytes).
output_len: Option<usize>,
}
impl Params {
/// Default memory cost.
pub const DEFAULT_M_COST: u32 = 19 * 1024;
/// Minimum number of 1 KiB memory blocks.
#[allow(clippy::cast_possible_truncation)]
pub const MIN_M_COST: u32 = 2 * SYNC_POINTS as u32; // 2 blocks per slice
/// Maximum number of 1 KiB memory blocks.
pub const MAX_M_COST: u32 = u32::MAX;
/// Default number of iterations (i.e. "time").
pub const DEFAULT_T_COST: u32 = 2;
/// Minimum number of passes.
pub const MIN_T_COST: u32 = 1;
/// Maximum number of passes.
pub const MAX_T_COST: u32 = u32::MAX;
/// Default degree of parallelism.
pub const DEFAULT_P_COST: u32 = 1;
/// Minimum and maximum number of threads (i.e. parallelism).
pub const MIN_P_COST: u32 = 1;
/// Minimum and maximum number of threads (i.e. parallelism).
pub const MAX_P_COST: u32 = 0xFFFFFF;
/// Maximum length of a key ID in bytes.
pub const MAX_KEYID_LEN: usize = 8;
/// Maximum length of associated data in bytes.
pub const MAX_DATA_LEN: usize = 32;
/// Default output length.
pub const DEFAULT_OUTPUT_LEN: usize = 32;
/// Minimum digest size in bytes.
pub const MIN_OUTPUT_LEN: usize = 4;
/// Maximum digest size in bytes.
pub const MAX_OUTPUT_LEN: usize = 0xFFFFFFFF;
/// Default parameters (recommended).
pub const DEFAULT: Self = Params {
m_cost: Self::DEFAULT_M_COST,
t_cost: Self::DEFAULT_T_COST,
p_cost: Self::DEFAULT_P_COST,
keyid: KeyId {
bytes: [0u8; Self::MAX_KEYID_LEN],
len: 0,
},
data: AssociatedData {
bytes: [0u8; Self::MAX_DATA_LEN],
len: 0,
},
output_len: None,
};
/// Create new parameters.
///
/// # Arguments
/// - `m_cost`: memory size in 1 KiB blocks. Between 8\*`p_cost` and (2^32)-1.
/// - `t_cost`: number of iterations. Between 1 and (2^32)-1.
/// - `p_cost`: degree of parallelism. Between 1 and (2^24)-1.
/// - `output_len`: size of the KDF output in bytes. Default 32.
pub const fn new(
m_cost: u32,
t_cost: u32,
p_cost: u32,
output_len: Option<usize>,
) -> Result<Self> {
if m_cost < Params::MIN_M_COST {
return Err(Error::MemoryTooLittle);
}
// Note: we don't need to check `MAX_M_COST`, since it's `u32::MAX`
if m_cost < p_cost * 8 {
return Err(Error::MemoryTooLittle);
}
if t_cost < Params::MIN_T_COST {
return Err(Error::TimeTooSmall);
}
// Note: we don't need to check `MAX_T_COST`, since it's `u32::MAX`
if p_cost < Params::MIN_P_COST {
return Err(Error::ThreadsTooFew);
}
if p_cost > Params::MAX_P_COST {
return Err(Error::ThreadsTooMany);
}
if let Some(len) = output_len {
if len < Params::MIN_OUTPUT_LEN {
return Err(Error::OutputTooShort);
}
if len > Params::MAX_OUTPUT_LEN {
return Err(Error::OutputTooLong);
}
}
Ok(Params {
m_cost,
t_cost,
p_cost,
keyid: KeyId::EMPTY,
data: AssociatedData::EMPTY,
output_len,
})
}
/// Memory size, expressed in kibibytes. Between 8\*`p_cost` and (2^32)-1.
///
/// Value is an integer in decimal (1 to 10 digits).
pub const fn m_cost(&self) -> u32 {
self.m_cost
}
/// Number of iterations. Between 1 and (2^32)-1.
///
/// Value is an integer in decimal (1 to 10 digits).
pub const fn t_cost(&self) -> u32 {
self.t_cost
}
/// Degree of parallelism. Between 1 and (2^24)-1.
///
/// Value is an integer in decimal (1 to 3 digits).
pub const fn p_cost(&self) -> u32 {
self.p_cost
}
/// Key identifier: byte slice between 0 and 8 bytes in length.
///
/// Defaults to an empty byte slice.
///
/// Note this field is only present as a helper for reading/storing in
/// the PHC hash string format (i.e. it is totally ignored from a
/// cryptographical standpoint).
///
/// On top of that, this field is not longer part of the Argon2 standard
/// (see: <https://github.com/P-H-C/phc-winner-argon2/pull/173>), and should
/// not be used for any non-legacy work.
pub fn keyid(&self) -> &[u8] {
self.keyid.as_bytes()
}
/// Associated data: byte slice between 0 and 32 bytes in length.
///
/// Defaults to an empty byte slice.
///
/// This field is not longer part of the argon2 standard
/// (see: <https://github.com/P-H-C/phc-winner-argon2/pull/173>), and should
/// not be used for any non-legacy work.
pub fn data(&self) -> &[u8] {
self.data.as_bytes()
}
/// Length of the output (in bytes).
pub const fn output_len(&self) -> Option<usize> {
self.output_len
}
/// Get the number of lanes.
#[allow(clippy::cast_possible_truncation)]
pub(crate) const fn lanes(&self) -> usize {
self.p_cost as usize
}
/// Get the number of blocks in a lane.
pub(crate) const fn lane_length(&self) -> usize {
self.segment_length() * SYNC_POINTS
}
/// Get the segment length given the configured `m_cost` and `p_cost`.
///
/// Minimum memory_blocks = 8*`L` blocks, where `L` is the number of lanes.
pub(crate) const fn segment_length(&self) -> usize {
let m_cost = self.m_cost as usize;
let memory_blocks = if m_cost < 2 * SYNC_POINTS * self.lanes() {
2 * SYNC_POINTS * self.lanes()
} else {
m_cost
};
memory_blocks / (self.lanes() * SYNC_POINTS)
}
/// Get the number of blocks required given the configured `m_cost` and `p_cost`.
pub const fn block_count(&self) -> usize {
self.segment_length() * self.lanes() * SYNC_POINTS
}
}
impl Default for Params {
fn default() -> Params {
Params::DEFAULT
}
}
macro_rules! param_buf {
($ty:ident, $name:expr, $max_len:expr, $error:expr, $doc:expr) => {
#[doc = $doc]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct $ty {
/// Byte array
bytes: [u8; Self::MAX_LEN],
/// Length of byte array
len: usize,
}
impl $ty {
/// Maximum length in bytes
pub const MAX_LEN: usize = $max_len;
#[doc = "Create a new"]
#[doc = $name]
#[doc = "from a slice."]
pub fn new(slice: &[u8]) -> Result<Self> {
let mut bytes = [0u8; Self::MAX_LEN];
let len = slice.len();
bytes.get_mut(..len).ok_or($error)?.copy_from_slice(slice);
Ok(Self { bytes, len })
}
/// Empty value.
pub const EMPTY: Self = Self {
bytes: [0u8; Self::MAX_LEN],
len: 0,
};
#[doc = "Decode"]
#[doc = $name]
#[doc = " from a B64 string"]
pub fn from_b64(s: &str) -> Result<Self> {
let mut bytes = [0u8; Self::MAX_LEN];
let len = B64::decode(s, &mut bytes)?.len();
Ok(Self { bytes, len })
}
/// Borrow the inner value as a byte slice.
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len]
}
/// Get the length in bytes.
pub const fn len(&self) -> usize {
self.len
}
/// Is this value empty?
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl AsRef<[u8]> for $ty {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl FromStr for $ty {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::from_b64(s)
}
}
impl TryFrom<&[u8]> for $ty {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self> {
Self::new(bytes)
}
}
};
}
// KeyId
param_buf!(
KeyId,
"KeyId",
Params::MAX_KEYID_LEN,
Error::KeyIdTooLong,
"Key identifier"
);
// AssociatedData
param_buf!(
AssociatedData,
"AssociatedData",
Params::MAX_DATA_LEN,
Error::AdTooLong,
"Associated data"
);
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl<'a> TryFrom<&'a PasswordHash<'a>> for Params {
type Error = password_hash::Error;
fn try_from(hash: &'a PasswordHash<'a>) -> password_hash::Result<Self> {
let mut builder = ParamsBuilder::new();
for (ident, value) in hash.params.iter() {
match ident.as_str() {
"m" => {
builder.m_cost(value.decimal()?);
}
"t" => {
builder.t_cost(value.decimal()?);
}
"p" => {
builder.p_cost(value.decimal()?);
}
"keyid" => {
builder.keyid(value.as_str().parse()?);
}
"data" => {
builder.data(value.as_str().parse()?);
}
_ => return Err(password_hash::Error::ParamNameInvalid),
}
}
if let Some(output) = &hash.hash {
builder.output_len(output.len());
}
Ok(builder.build()?)
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl TryFrom<Params> for ParamsString {
type Error = password_hash::Error;
fn try_from(params: Params) -> password_hash::Result<ParamsString> {
ParamsString::try_from(&params)
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl TryFrom<&Params> for ParamsString {
type Error = password_hash::Error;
fn try_from(params: &Params) -> password_hash::Result<ParamsString> {
let mut output = ParamsString::new();
output.add_decimal("m", params.m_cost)?;
output.add_decimal("t", params.t_cost)?;
output.add_decimal("p", params.p_cost)?;
if !params.keyid.is_empty() {
output.add_b64_bytes("keyid", params.keyid.as_bytes())?;
}
if !params.data.is_empty() {
output.add_b64_bytes("data", params.data.as_bytes())?;
}
Ok(output)
}
}
/// Builder for Argon2 [`Params`].
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParamsBuilder {
m_cost: u32,
t_cost: u32,
p_cost: u32,
keyid: Option<KeyId>,
data: Option<AssociatedData>,
output_len: Option<usize>,
}
impl ParamsBuilder {
/// Create a new builder with the default parameters.
pub const fn new() -> Self {
Self::DEFAULT
}
/// Set memory size, expressed in kibibytes, between 8\*`p_cost` and (2^32)-1.
pub fn m_cost(&mut self, m_cost: u32) -> &mut Self {
self.m_cost = m_cost;
self
}
/// Set number of iterations, between 1 and (2^32)-1.
pub fn t_cost(&mut self, t_cost: u32) -> &mut Self {
self.t_cost = t_cost;
self
}
/// Set degree of parallelism, between 1 and (2^24)-1.
pub fn p_cost(&mut self, p_cost: u32) -> &mut Self {
self.p_cost = p_cost;
self
}
/// Set key identifier.
pub fn keyid(&mut self, keyid: KeyId) -> &mut Self {
self.keyid = Some(keyid);
self
}
/// Set associated data.
pub fn data(&mut self, data: AssociatedData) -> &mut Self {
self.data = Some(data);
self
}
/// Set length of the output (in bytes).
pub fn output_len(&mut self, len: usize) -> &mut Self {
self.output_len = Some(len);
self
}
/// Get the finished [`Params`].
///
/// This performs validations to ensure that the given parameters are valid
/// and compatible with each other, and will return an error if they are not.
pub const fn build(&self) -> Result<Params> {
let mut params = match Params::new(self.m_cost, self.t_cost, self.p_cost, self.output_len) {
Ok(params) => params,
Err(err) => return Err(err),
};
if let Some(keyid) = self.keyid {
params.keyid = keyid;
}
if let Some(data) = self.data {
params.data = data;
};
Ok(params)
}
/// Create a new [`Argon2`] context using the provided algorithm/version.
pub fn context(&self, algorithm: Algorithm, version: Version) -> Result<Argon2<'_>> {
Ok(Argon2::new(algorithm, version, self.build()?))
}
/// Default parameters (recommended).
pub const DEFAULT: ParamsBuilder = {
let params = Params::DEFAULT;
Self {
m_cost: params.m_cost,
t_cost: params.t_cost,
p_cost: params.p_cost,
keyid: None,
data: None,
output_len: params.output_len,
}
};
}
impl Default for ParamsBuilder {
fn default() -> Self {
Self::DEFAULT
}
}
impl TryFrom<ParamsBuilder> for Params {
type Error = Error;
fn try_from(builder: ParamsBuilder) -> Result<Params> {
builder.build()
}
}
#[cfg(all(test, feature = "alloc", feature = "password-hash"))]
mod tests {
use super::*;
#[test]
fn params_builder_bad_values() {
assert_eq!(
ParamsBuilder::new().m_cost(Params::MIN_M_COST - 1).build(),
Err(Error::MemoryTooLittle)
);
assert_eq!(
ParamsBuilder::new().t_cost(Params::MIN_T_COST - 1).build(),
Err(Error::TimeTooSmall)
);
assert_eq!(
ParamsBuilder::new().p_cost(Params::MIN_P_COST - 1).build(),
Err(Error::ThreadsTooFew)
);
assert_eq!(
ParamsBuilder::new()
.m_cost(Params::DEFAULT_P_COST * 8 - 1)
.build(),
Err(Error::MemoryTooLittle)
);
assert_eq!(
ParamsBuilder::new()
.m_cost((Params::MAX_P_COST + 1) * 8)
.p_cost(Params::MAX_P_COST + 1)
.build(),
Err(Error::ThreadsTooMany)
);
}
#[test]
fn associated_data_too_long() {
let ret = AssociatedData::new(&[0u8; Params::MAX_DATA_LEN + 1]);
assert_eq!(ret, Err(Error::AdTooLong));
}
#[test]
fn keyid_too_long() {
let ret = KeyId::new(&[0u8; Params::MAX_KEYID_LEN + 1]);
assert_eq!(ret, Err(Error::KeyIdTooLong));
}
}

44
vendor/argon2/src/version.rs vendored Normal file
View File

@@ -0,0 +1,44 @@
//! Version of the algorithm.
use crate::{Error, Result};
/// Version of the algorithm.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Version {
/// Version 16 (0x10 in hex)
///
/// Performs overwrite internally
V0x10 = 0x10,
/// Version 19 (0x13 in hex, default)
///
/// Performs XOR internally
#[default]
V0x13 = 0x13,
}
impl Version {
/// Serialize version as little endian bytes
pub(crate) const fn to_le_bytes(self) -> [u8; 4] {
(self as u32).to_le_bytes()
}
}
impl From<Version> for u32 {
fn from(version: Version) -> u32 {
version as u32
}
}
impl TryFrom<u32> for Version {
type Error = Error;
fn try_from(version_id: u32) -> Result<Version> {
match version_id {
0x10 => Ok(Version::V0x10),
0x13 => Ok(Version::V0x13),
_ => Err(Error::VersionInvalid),
}
}
}