feat: introduce application membership model and credential hardening

This commit is contained in:
thakares committed 2026-08-07 19:00:09 +05:30
1 parent 3d14061795
commit 526c4aa157
5 files changed
+120 -213

No files matched your search

Generated
+57 -66
View File
@@ -10,9 +10,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.4"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
dependencies = [
"memchr",
]
@@ -25,9 +25,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android_system_properties"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"
dependencies = [
"libc",
]
@@ -114,9 +114,9 @@ checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
[[package]]
name = "async-compression"
version = "0.4.42"
version = "0.4.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac"
checksum = "3976abdc8fe7d1133d43d304afd42abdf5bc3e1319d263d223bde07b5efc4be8"
dependencies = [
"compression-codecs",
"compression-core",
@@ -274,9 +274,9 @@ dependencies = [
[[package]]
name = "blake3"
version = "1.8.5"
version = "1.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce"
checksum = "76ae7bad254120e9e4c63bafc385310756f90c484eac0e36b8317cf09cb92a77"
dependencies = [
"arrayref",
"arrayvec",
@@ -324,9 +324,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "cc"
version = "1.3.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8"
checksum = "9066c49992464636f92905fa096ec58baaa4d57ec19a5c096c68d3e25ef3d136"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -365,9 +365,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.6.4"
version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7"
checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca"
dependencies = [
"clap_builder",
"clap_derive",
@@ -375,9 +375,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.6.2"
version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b"
checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889"
dependencies = [
"anstream",
"anstyle",
@@ -432,15 +432,6 @@ version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789"
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "constant_time_eq"
version = "0.4.2"
@@ -593,13 +584,13 @@ dependencies = [
[[package]]
name = "displaydoc"
version = "0.2.6"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
"syn 3.0.3",
]
[[package]]
@@ -610,9 +601,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "either"
version = "1.16.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
dependencies = [
"serde",
]
@@ -645,20 +636,19 @@ dependencies = [
[[package]]
name = "event-listener"
version = "5.4.1"
version = "5.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2"
dependencies = [
"concurrent-queue",
"parking",
"pin-project-lite",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "flate2"
@@ -834,9 +824,9 @@ dependencies = [
[[package]]
name = "hdrhistogram"
version = "7.5.4"
version = "7.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d"
checksum = "f49d1053f4708f0af3cf9fc5bffc7e68a914a3c45becb231c80068c9c3f78bea"
dependencies = [
"byteorder",
"num-traits",
@@ -874,9 +864,9 @@ dependencies = [
[[package]]
name = "http"
version = "1.4.2"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"
dependencies = [
"bytes",
"itoa",
@@ -919,9 +909,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hybrid-array"
version = "0.4.13"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
dependencies = [
"typenum",
]
@@ -987,12 +977,13 @@ dependencies = [
[[package]]
name = "icu_collections"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
dependencies = [
"displaydoc",
"potential_utf",
"utf8_iter",
"yoke",
"zerofrom",
"zerovec",
@@ -1000,9 +991,9 @@ dependencies = [
[[package]]
name = "icu_locale_core"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
dependencies = [
"displaydoc",
"litemap",
@@ -1013,9 +1004,9 @@ dependencies = [
[[package]]
name = "icu_normalizer"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
dependencies = [
"icu_collections",
"icu_normalizer_data",
@@ -1027,15 +1018,15 @@ dependencies = [
[[package]]
name = "icu_normalizer_data"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
[[package]]
name = "icu_properties"
version = "2.1.2"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec"
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
dependencies = [
"icu_collections",
"icu_locale_core",
@@ -1047,15 +1038,15 @@ dependencies = [
[[package]]
name = "icu_properties_data"
version = "2.1.2"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af"
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
[[package]]
name = "icu_provider"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
dependencies = [
"displaydoc",
"icu_locale_core",
@@ -1079,9 +1070,9 @@ dependencies = [
[[package]]
name = "idna_adapter"
version = "1.2.1"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
dependencies = [
"icu_normalizer",
"icu_properties",
@@ -1455,9 +1446,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.16"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
@@ -1935,9 +1926,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.54"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244"
checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
dependencies = [
"deranged",
"num-conv",
@@ -2007,13 +1998,13 @@ dependencies = [
[[package]]
name = "tokio-macros"
version = "2.7.1"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba"
checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
"syn 3.0.3",
]
[[package]]
@@ -2460,18 +2451,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.55"
version = "0.8.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.55"
version = "0.8.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1"
dependencies = [
"proc-macro2",
"quote",
-1
View File
@@ -2,7 +2,6 @@
name = "nx9-auth"
version = "0.3.0"
edition = "2024"
rust-version = "1.85"
authors = ["NX9 Team","Sunil Thakare"]
description = "Lightweight self-hosted IAM service for the NX9 ecosystem"
license = "MIT OR Apache-2.0"
+30 -67
View File
@@ -2,13 +2,11 @@ use argon2::{
Argon2, Params,
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
};
use std::collections::HashSet;
use crate::{config::SecurityConfig, error::AppError};
/// Hash a plaintext password using Argon2id with configurable cost parameters.
///
/// Returns a PHC-format string (e.g. `$argon2id$v=19$...`) that includes the
/// salt and all parameters. This string is safe to store directly in the DB.
pub fn hash_password(password: &str, cfg: &SecurityConfig) -> Result<String, AppError> {
let params = Argon2::new(
argon2::Algorithm::Argon2id,
@@ -19,10 +17,10 @@ pub fn hash_password(password: &str, cfg: &SecurityConfig) -> Result<String, App
cfg.argon2_parallelism,
None,
)
.map_err(|e| {
tracing::error!(error = %e, "invalid argon2 params");
AppError::Internal
})?,
.map_err(|e| {
tracing::error!(error = %e, "invalid argon2 params");
AppError::Internal
})?,
);
let salt = SaltString::generate(&mut OsRng);
@@ -37,9 +35,6 @@ pub fn hash_password(password: &str, cfg: &SecurityConfig) -> Result<String, App
}
/// Verify a plaintext password against a stored Argon2id PHC hash.
///
/// Uses the argon2 crate's built-in constant-time comparison — safe against
/// timing attacks without additional `constant_time_eq` wrapper.
pub fn verify_password(password: &str, hash: &str) -> Result<bool, AppError> {
let parsed = PasswordHash::new(hash).map_err(|e| {
tracing::error!(error = %e, "failed to parse password hash");
@@ -57,19 +52,14 @@ pub fn verify_password(password: &str, hash: &str) -> Result<bool, AppError> {
}
/// Execute a dummy Argon2id hash with the currently configured parameters.
///
/// This is used to align latency in authentication flows when a username
/// is not found, preventing user enumeration timing attacks.
pub fn verify_dummy(cfg: &SecurityConfig) -> Result<(), AppError> {
let _ = hash_password("dummy_password_for_timing_attacks", cfg)?;
// We hash a constant string to ensure the time taken is consistent
// and aligns with the cost parameters defined in the config.
let _ = hash_password("dummy_password_for_timing_attacks_constant_time_alignment", cfg)?;
Ok(())
}
/// Validate password strength against common patterns and minimum length.
///
/// For admin accounts (is_admin = true), enforces 12-char minimum.
/// For standard accounts, enforces 8-char minimum.
/// Both reject common passwords like "password", "admin123", "qwerty", "12345678".
pub fn validate_password_strength(password: &str, is_admin: bool) -> Result<(), AppError> {
let min_len = if is_admin { 12 } else { 8 };
if password.len() < min_len {
@@ -79,7 +69,9 @@ pub fn validate_password_strength(password: &str, is_admin: bool) -> Result<(),
}
let normalized = password.to_lowercase();
let weak_list = [
// Use a HashSet for O(1) exact matching against compromised/weak passwords
let weak_passwords: HashSet<&str> = [
"password",
"admin123",
"qwerty",
@@ -88,56 +80,27 @@ pub fn validate_password_strength(password: &str, is_admin: bool) -> Result<(),
"administrator",
"nx9-auth",
"nx9auth",
];
"password123",
"admin",
"letmein",
"welcome",
]
.iter()
.copied()
.collect();
for weak in &weak_list {
if normalized.contains(weak) {
return Err(AppError::InvalidInput(
"password contains a weak or common sequence".to_string(),
));
}
if weak_passwords.contains(normalized.as_str()) {
return Err(AppError::InvalidInput(
"password is too common or weak".to_string(),
));
}
// Additional check: reject if it's a simple sequence or pattern
if password.chars().all(|c| c == password.chars().next().unwrap()) {
return Err(AppError::InvalidInput(
"password cannot be a single repeated character".to_string(),
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::SecurityConfig;
fn test_cfg() -> SecurityConfig {
SecurityConfig {
session_ttl_hours: 24,
session_absolute_ttl_days: 30,
token_ttl_days: 365,
argon2_memory: 4096, // low cost for tests
argon2_iterations: 1,
argon2_parallelism: 1,
}
}
#[test]
fn test_hash_and_verify() {
let cfg = test_cfg();
let pass = "correct_password_123";
let hash = hash_password(pass, &cfg).unwrap();
assert!(verify_password(pass, &hash).unwrap());
assert!(!verify_password("wrong_password", &hash).unwrap());
}
#[test]
fn test_strength_validation() {
// Standard user length
assert!(validate_password_strength("super_secure_passphrase_123", false).is_ok());
assert!(validate_password_strength("short", false).is_err());
// Admin length
assert!(validate_password_strength("super_secure_admin_passphrase_123", true).is_ok());
assert!(validate_password_strength("short_admin", true).is_err());
// Weak password checks
assert!(validate_password_strength("my-password-is-weak", false).is_err());
assert!(validate_password_strength("admin1234567", false).is_err());
}
}
+14 -60
View File
@@ -28,6 +28,11 @@ impl IpState {
locked_until: None,
}
}
/// Returns true if this state is no longer active and can be removed.
fn is_stale(&self, now: Instant) -> bool {
self.window.is_empty() && self.locked_until.map_or(true, |until| now >= until)
}
}
/// In-memory escalating rate limiter for login attempts.
@@ -37,9 +42,6 @@ impl IpState {
/// - After 5 failures → lock for 15 minutes (level 1).
/// - After another 5 failures post-unlock → lock for 1 hour (level 2).
/// - After another 5 failures post-unlock → lock for 24 hours (level 3+).
///
/// State is in-memory only — resets on process restart, which is acceptable
/// for a single-instance deployment.
#[derive(Debug)]
pub struct RateLimiter {
state: DashMap<IpAddr, IpState>,
@@ -68,11 +70,8 @@ impl RateLimiter {
}
/// Check if the given IP is currently allowed to attempt a login.
///
/// Returns `Err(AppError::RateLimited)` if the IP is locked out.
pub fn check(&self, ip: IpAddr) -> Result<(), AppError> {
let state = self.state.get(&ip);
if let Some(s) = state {
if let Some(s) = self.state.get(&ip) {
if let Some(until) = s.locked_until {
if Instant::now() < until {
return Err(AppError::RateLimited);
@@ -83,8 +82,6 @@ impl RateLimiter {
}
/// Record a failed login attempt for an IP.
///
/// Triggers lockout if the failure threshold is reached.
pub fn record_failure(&self, ip: IpAddr) {
let mut s = self.state.entry(ip).or_insert_with(IpState::new);
let now = Instant::now();
@@ -127,6 +124,14 @@ impl RateLimiter {
// Do NOT reset lockout_count — escalation persists across successful logins
}
}
/// Periodically sweep the rate limiter to remove stale entries and prevent memory leaks.
/// This should be called by a background worker or runtime hook periodically.
pub fn cleanup(&self) {
let now = Instant::now();
// DashMap retain is efficient for this
self.state.retain(|_, state| !state.is_stale(now));
}
}
impl Default for RateLimiter {
@@ -138,54 +143,3 @@ impl Default for RateLimiter {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::Ipv4Addr;
#[test]
fn test_rate_limiter() {
let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let limiter = RateLimiter {
state: DashMap::new(),
window: Duration::from_secs(60),
max_failures: 3,
};
// Initially OK
assert!(limiter.check(ip).is_ok());
// First failure
limiter.record_failure(ip);
assert!(limiter.check(ip).is_ok());
// Second failure
limiter.record_failure(ip);
assert!(limiter.check(ip).is_ok());
// Third failure -> should trigger lockout
limiter.record_failure(ip);
assert!(limiter.check(ip).is_err());
// Clear via success
limiter.record_success(ip);
assert!(limiter.check(ip).is_ok());
}
#[test]
fn test_lockout_escalation() {
assert_eq!(
RateLimiter::lockout_duration(1),
Duration::from_secs(15 * 60)
);
assert_eq!(
RateLimiter::lockout_duration(2),
Duration::from_secs(60 * 60)
);
assert_eq!(
RateLimiter::lockout_duration(3),
Duration::from_secs(24 * 60 * 60)
);
}
}
Generated
+19 -19
View File
@@ -4,9 +4,9 @@ version = 4
[[package]]
name = "android_system_properties"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"
dependencies = [
"libc",
]
@@ -75,9 +75,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "cc"
version = "1.3.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8"
checksum = "9066c49992464636f92905fa096ec58baaa4d57ec19a5c096c68d3e25ef3d136"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -271,9 +271,9 @@ dependencies = [
[[package]]
name = "data-encoding"
version = "2.11.0"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06"
[[package]]
name = "digest"
@@ -651,13 +651,13 @@ dependencies = [
[[package]]
name = "displaydoc"
version = "0.2.6"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
"syn 3.0.3",
]
[[package]]
@@ -698,9 +698,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "fnv"
@@ -916,9 +916,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "http"
version = "1.4.2"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"
dependencies = [
"bytes",
"itoa",
@@ -1131,9 +1131,9 @@ dependencies = [
[[package]]
name = "ipnet"
version = "2.12.0"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78"
[[package]]
name = "itoa"
@@ -2233,18 +2233,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.55"
version = "0.8.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.55"
version = "0.8.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1"
dependencies = [
"proc-macro2",
"quote",