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

27
vendor/scrypt/src/errors.rs vendored Normal file
View File

@@ -0,0 +1,27 @@
use core::fmt;
/// `scrypt()` error
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct InvalidOutputLen;
/// `ScryptParams` error
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct InvalidParams;
impl fmt::Display for InvalidOutputLen {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid output buffer length")
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidOutputLen {}
impl fmt::Display for InvalidParams {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid scrypt parameters")
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidParams {}

120
vendor/scrypt/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,120 @@
//! This crate implements the Scrypt key derivation function as specified
//! in \[1\].
//!
//! If you are only using the low-level [`scrypt`] function instead of the
//! higher-level [`Scrypt`] struct to produce/verify hash strings,
//! it's recommended to disable default features in your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! scrypt = { version = "0.2", default-features = false }
//! ```
//!
//! # Usage (simple with default params)
//!
//! ```
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # #[cfg(all(feature = "simple", feature = "std"))]
//! # {
//! use scrypt::{
//! password_hash::{
//! rand_core::OsRng,
//! PasswordHash, PasswordHasher, PasswordVerifier, SaltString
//! },
//! Scrypt
//! };
//!
//! let password = b"hunter42"; // Bad password; don't actually use!
//! let salt = SaltString::generate(&mut OsRng);
//!
//! // Hash password to PHC string ($scrypt$...)
//! let password_hash = Scrypt.hash_password(password, &salt)?.to_string();
//!
//! // Verify password against PHC string
//! let parsed_hash = PasswordHash::new(&password_hash)?;
//! assert!(Scrypt.verify_password(password, &parsed_hash).is_ok());
//! # }
//! # Ok(())
//! # }
//! ```
//!
//! # References
//! \[1\] - [C. Percival. Stronger Key Derivation Via Sequential
//! Memory-Hard Functions](http://www.tarsnap.com/scrypt/scrypt.pdf)
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![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"
)]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use pbkdf2::pbkdf2_hmac;
use sha2::Sha256;
/// Errors for `scrypt` operations.
pub mod errors;
mod params;
mod romix;
#[cfg(feature = "simple")]
mod simple;
pub use crate::params::Params;
#[cfg(feature = "simple")]
pub use password_hash;
#[cfg(feature = "simple")]
pub use crate::simple::{Scrypt, ALG_ID};
/// The scrypt key derivation function.
///
/// # Arguments
/// - `password` - The password to process as a byte vector
/// - `salt` - The salt value to use as a byte vector
/// - `params` - The ScryptParams to use
/// - `output` - The resulting derived key is returned in this byte vector.
/// **WARNING: Make sure to compare this value in constant time!**
///
/// # Return
/// `Ok(())` if calculation is successful and `Err(InvalidOutputLen)` if
/// `output` does not satisfy the following condition:
/// `output.len() > 0 && output.len() <= (2^32 - 1) * 32`.
pub fn scrypt(
password: &[u8],
salt: &[u8],
params: &Params,
output: &mut [u8],
) -> Result<(), errors::InvalidOutputLen> {
// This check required by Scrypt:
// check output.len() > 0 && output.len() <= (2^32 - 1) * 32
if output.is_empty() || output.len() / 32 > 0xffff_ffff {
return Err(errors::InvalidOutputLen);
}
// The checks in the ScryptParams constructor guarantee
// that the following is safe:
let n = 1 << params.log_n;
let r128 = (params.r as usize) * 128;
let pr128 = (params.p as usize) * r128;
let nr128 = n * r128;
let mut b = vec![0u8; pr128];
pbkdf2_hmac::<Sha256>(password, salt, 1, &mut b);
let mut v = vec![0u8; nr128];
let mut t = vec![0u8; r128];
for chunk in &mut b.chunks_mut(r128) {
romix::scrypt_ro_mix(chunk, &mut v, &mut t, n);
}
pbkdf2_hmac::<Sha256>(password, &b, 1, output);
Ok(())
}

178
vendor/scrypt/src/params.rs vendored Normal file
View File

@@ -0,0 +1,178 @@
use core::mem::size_of;
use crate::errors::InvalidParams;
#[cfg(feature = "simple")]
use password_hash::{errors::InvalidValue, Error, ParamsString, PasswordHash};
/// The Scrypt parameter values.
#[derive(Clone, Copy, Debug)]
pub struct Params {
pub(crate) log_n: u8,
pub(crate) r: u32,
pub(crate) p: u32,
#[allow(dead_code)] // this field is used only with the `PasswordHasher` impl
pub(crate) len: usize,
}
impl Params {
/// Recommended log₂ of the Scrypt parameter `N`: CPU/memory cost.
pub const RECOMMENDED_LOG_N: u8 = 17;
/// Recommended Scrypt parameter `r`: block size.
pub const RECOMMENDED_R: u32 = 8;
/// Recommended Scrypt parameter `p`: parallelism.
pub const RECOMMENDED_P: u32 = 1;
/// Recommended Scrypt parameter `Key length`.
pub const RECOMMENDED_LEN: usize = 32;
/// Create a new instance of [`Params`].
///
/// # Arguments
/// - `log_n` - The log₂ of the Scrypt parameter `N`
/// - `r` - The Scrypt parameter `r`
/// - `p` - The Scrypt parameter `p`
/// - `len` - The Scrypt parameter `Key length`
///
/// # Conditions
/// - `log_n` must be less than `64`
/// - `r` must be greater than `0` and less than or equal to `4294967295`
/// - `p` must be greater than `0` and less than `4294967295`
/// - `len` must be greater than `9` and less than or equal to `64`
pub fn new(log_n: u8, r: u32, p: u32, len: usize) -> Result<Params, InvalidParams> {
let cond1 = (log_n as usize) < usize::BITS as usize;
let cond2 = size_of::<usize>() >= size_of::<u32>();
let cond3 = r <= usize::MAX as u32 && p < usize::MAX as u32;
let cond4 = (10..=64).contains(&len);
if !(r > 0 && p > 0 && cond1 && (cond2 || cond3) && cond4) {
return Err(InvalidParams);
}
let r = r as usize;
let p = p as usize;
let n: usize = 1 << log_n;
// check that r * 128 doesn't overflow
let r128 = r.checked_mul(128).ok_or(InvalidParams)?;
// check that n * r * 128 doesn't overflow
r128.checked_mul(n).ok_or(InvalidParams)?;
// check that p * r * 128 doesn't overflow
r128.checked_mul(p).ok_or(InvalidParams)?;
// This check required by Scrypt:
// check: n < 2^(128 * r / 8)
// r * 16 won't overflow since r128 didn't
if (log_n as usize) >= r * 16 {
return Err(InvalidParams);
}
// This check required by Scrypt:
// check: p <= ((2^32-1) * 32) / (128 * r)
// It takes a bit of re-arranging to get the check above into this form,
// but it is indeed the same.
if r * p >= 0x4000_0000 {
return Err(InvalidParams);
}
Ok(Params {
log_n,
r: r as u32,
p: p as u32,
len,
})
}
/// Recommended values sufficient for most use-cases
/// - `log_n = 15` (`n = 32768`)
/// - `r = 8`
/// - `p = 1`
pub fn recommended() -> Params {
Params {
log_n: Self::RECOMMENDED_LOG_N,
r: Self::RECOMMENDED_R,
p: Self::RECOMMENDED_P,
len: Self::RECOMMENDED_LEN,
}
}
/// log₂ of the Scrypt parameter `N`, the work factor.
///
/// Memory and CPU usage scale linearly with `N`.
pub fn log_n(&self) -> u8 {
self.log_n
}
/// `r` parameter: resource usage.
///
/// scrypt iterates 2*r times. Memory and CPU time scale linearly
/// with this parameter.
pub fn r(&self) -> u32 {
self.r
}
/// `p` parameter: parallelization.
pub fn p(&self) -> u32 {
self.p
}
}
impl Default for Params {
fn default() -> Params {
Params::recommended()
}
}
#[cfg(feature = "simple")]
#[cfg_attr(docsrs, doc(cfg(feature = "simple")))]
impl<'a> TryFrom<&'a PasswordHash<'a>> for Params {
type Error = password_hash::Error;
fn try_from(hash: &'a PasswordHash<'a>) -> Result<Self, password_hash::Error> {
let mut log_n = Self::RECOMMENDED_LOG_N;
let mut r = Self::RECOMMENDED_R;
let mut p = Self::RECOMMENDED_P;
if hash.version.is_some() {
return Err(Error::Version);
}
for (ident, value) in hash.params.iter() {
match ident.as_str() {
"ln" => {
log_n = value
.decimal()?
.try_into()
.map_err(|_| InvalidValue::Malformed.param_error())?
}
"r" => r = value.decimal()?,
"p" => p = value.decimal()?,
_ => return Err(password_hash::Error::ParamNameInvalid),
}
}
let len = hash
.hash
.map(|out| out.len())
.unwrap_or(Self::RECOMMENDED_LEN);
Params::new(log_n, r, p, len).map_err(|_| InvalidValue::Malformed.param_error())
}
}
#[cfg(feature = "simple")]
#[cfg_attr(docsrs, doc(cfg(feature = "simple")))]
impl TryFrom<Params> for ParamsString {
type Error = password_hash::Error;
fn try_from(input: Params) -> Result<ParamsString, password_hash::Error> {
let mut output = ParamsString::new();
output.add_decimal("ln", input.log_n as u32)?;
output.add_decimal("r", input.r)?;
output.add_decimal("p", input.p)?;
Ok(output)
}
}

74
vendor/scrypt/src/romix.rs vendored Normal file
View File

@@ -0,0 +1,74 @@
/// Execute the ROMix operation in-place.
/// b - the data to operate on
/// v - a temporary variable to store the vector V
/// t - a temporary variable to store the result of the xor
/// n - the scrypt parameter N
#[allow(clippy::many_single_char_names)]
pub(crate) fn scrypt_ro_mix(b: &mut [u8], v: &mut [u8], t: &mut [u8], n: usize) {
fn integerify(x: &[u8], n: usize) -> usize {
// n is a power of 2, so n - 1 gives us a bitmask that we can use to perform a calculation
// mod n using a simple bitwise and.
let mask = n - 1;
// This cast is safe since we're going to get the value mod n (which is a power of 2), so we
// don't have to care about truncating any of the high bits off
//let result = (LittleEndian::read_u32(&x[x.len() - 64..x.len() - 60]) as usize) & mask;
let t = u32::from_le_bytes(x[x.len() - 64..x.len() - 60].try_into().unwrap());
(t as usize) & mask
}
let len = b.len();
for chunk in v.chunks_mut(len) {
chunk.copy_from_slice(b);
scrypt_block_mix(chunk, b);
}
for _ in 0..n {
let j = integerify(b, n);
xor(b, &v[j * len..(j + 1) * len], t);
scrypt_block_mix(t, b);
}
}
/// Execute the BlockMix operation
/// input - the input vector. The length must be a multiple of 128.
/// output - the output vector. Must be the same length as input.
fn scrypt_block_mix(input: &[u8], output: &mut [u8]) {
use salsa20::{
cipher::{typenum::U4, StreamCipherCore},
SalsaCore,
};
type Salsa20_8 = SalsaCore<U4>;
let mut x = [0u8; 64];
x.copy_from_slice(&input[input.len() - 64..]);
let mut t = [0u8; 64];
for (i, chunk) in input.chunks(64).enumerate() {
xor(&x, chunk, &mut t);
let mut t2 = [0u32; 16];
for (c, b) in t.chunks_exact(4).zip(t2.iter_mut()) {
*b = u32::from_le_bytes(c.try_into().unwrap());
}
Salsa20_8::from_raw_state(t2).write_keystream_block((&mut x).into());
let pos = if i % 2 == 0 {
(i / 2) * 64
} else {
(i / 2) * 64 + input.len() / 2
};
output[pos..pos + 64].copy_from_slice(&x);
}
}
fn xor(x: &[u8], y: &[u8], output: &mut [u8]) {
for ((out, &x_i), &y_i) in output.iter_mut().zip(x.iter()).zip(y.iter()) {
*out = x_i ^ y_i;
}
}

64
vendor/scrypt/src/simple.rs vendored Normal file
View File

@@ -0,0 +1,64 @@
//! Implementation of the `password-hash` crate API.
use crate::{scrypt, Params};
use core::cmp::Ordering;
use password_hash::{Decimal, Error, Ident, Output, PasswordHash, PasswordHasher, Result, Salt};
/// Algorithm identifier
pub const ALG_ID: Ident = Ident::new_unwrap("scrypt");
/// scrypt type for use with [`PasswordHasher`].
///
/// See the [crate docs](crate) for a usage example.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(docsrs, doc(cfg(feature = "simple")))]
pub struct Scrypt;
impl PasswordHasher for Scrypt {
type Params = Params;
fn hash_password_customized<'a>(
&self,
password: &[u8],
alg_id: Option<Ident<'a>>,
version: Option<Decimal>,
params: Params,
salt: impl Into<Salt<'a>>,
) -> Result<PasswordHash<'a>> {
if !matches!(alg_id, Some(ALG_ID) | None) {
return Err(Error::Algorithm);
}
// Versions unsupported
if version.is_some() {
return Err(Error::Version);
}
let salt = salt.into();
let mut salt_arr = [0u8; 64];
let salt_bytes = salt.decode_b64(&mut salt_arr)?;
let output = Output::init_with(params.len, |out| {
scrypt(password, salt_bytes, &params, out).map_err(|_| {
let provided = if out.is_empty() {
Ordering::Less
} else {
Ordering::Greater
};
password_hash::Error::OutputSize {
provided,
expected: 0, // TODO(tarcieri): calculate for `Ordering::Greater` case
}
})
})?;
Ok(PasswordHash {
algorithm: ALG_ID,
version: None,
params: params.try_into()?,
salt: Some(salt),
hash: Some(output),
})
}
}