Download rust-engine/src/validator.rs from wuhp/pqc: direct link, hf CLI and curl.
- Browser
- Download file 6.62 kB
-
https://huggingface.co/spaces/wuhp/pqc/resolve/main/rust-engine/src/validator.rs
- Command line
-
hf download hf://spaces/wuhp/pqc/rust-engine/src/validator.rs
-
curl -L -o validator.rs https://huggingface.co/spaces/wuhp/pqc/resolve/main/rust-engine/src/validator.rs
6.62 kB
| use serde::{Deserialize, Serialize}; | |
| pub enum Severity { | |
| Critical, | |
| Warning, | |
| Info, | |
| } | |
| pub struct ParamDiagnostic { | |
| pub severity: Severity, | |
| pub message: String, | |
| pub suggested_fix: Option<String>, | |
| } | |
| pub struct ValidationResult { | |
| pub n: usize, | |
| pub q: u64, | |
| pub beta_or_sigma: f64, | |
| pub is_cbd: bool, | |
| pub noise_budget_max: f64, | |
| pub noise_budget_rms: f64, | |
| pub decryption_budget: f64, | |
| pub failure_prob: f64, | |
| pub expected_failures_in_500: f64, | |
| pub passes: bool, | |
| pub diagnostics: Vec<ParamDiagnostic>, | |
| pub suggested_beta: f64, | |
| pub suggested_q: u64, | |
| } | |
| pub struct ParamValidator; | |
| impl ParamValidator { | |
| pub fn validate_lwe(n: usize, q: u64, beta: f64) -> ValidationResult { | |
| Self::validate_inner(n, q, beta, false) | |
| } | |
| pub fn validate_kyber(n: usize, q: u64, eta: usize) -> ValidationResult { | |
| let mut r = Self::validate_inner(n, q, eta as f64, true); | |
| r.beta_or_sigma = eta as f64; | |
| r | |
| } | |
| pub fn validate_gaussian(n: usize, q: u64, sigma: f64) -> ValidationResult { | |
| Self::validate_inner(n, q, sigma, false) | |
| } | |
| fn validate_inner(n: usize, q: u64, noise_param: f64, is_cbd: bool) -> ValidationResult { | |
| let q_f = q as f64; | |
| let n_f = n as f64; | |
| let std_per_coeff = if is_cbd { | |
| (noise_param / 2.0).sqrt() | |
| } else { | |
| noise_param | |
| }; | |
| let beta_max = if is_cbd { noise_param } else { 3.0 * std_per_coeff }; | |
| let noise_budget_rms = std_per_coeff * (3.0 * n_f).sqrt() * beta_max; | |
| let noise_budget_max = beta_max * beta_max * n_f * 3.0; | |
| let decryption_budget = q_f / 4.0; | |
| let total_std = std_per_coeff * beta_max * (3.0 * n_f).sqrt(); | |
| let z = decryption_budget / total_std.max(1e-10); | |
| let failure_prob = 2.0 * gaussian_q_function(z); | |
| let expected_failures_in_500 = failure_prob * 500.0; | |
| let passes = noise_budget_rms < decryption_budget * 0.8; | |
| let mut diagnostics = Vec::new(); | |
| if noise_budget_max >= decryption_budget && expected_failures_in_500 > 1e-3 { | |
| diagnostics.push(ParamDiagnostic { | |
| severity: Severity::Critical, | |
| message: format!( | |
| "WORST-CASE noise ({:.1}) EXCEEDS expected limit and probability of failure is high. Decryption WILL fail for some inputs.", | |
| noise_budget_max | |
| ), | |
| suggested_fix: Some(format!( | |
| "Either: (a) reduce β to ≤{:.1}, or (b) increase q to ≥{}", | |
| (decryption_budget / (n_f * 3.0)).sqrt(), | |
| (noise_budget_max * 4.0).ceil() as u64 | |
| )), | |
| }); | |
| } else if noise_budget_max >= decryption_budget { | |
| diagnostics.push(ParamDiagnostic { | |
| severity: Severity::Info, | |
| message: format!( | |
| "WORST-CASE noise bound ({:.1}) exceeds budget ({:.1}), but failure probability is low. Typical for practical RLWE.", | |
| noise_budget_max, decryption_budget | |
| ), | |
| suggested_fix: None, | |
| }); | |
| } else if noise_budget_max > decryption_budget * 0.85 { | |
| diagnostics.push(ParamDiagnostic { | |
| severity: Severity::Warning, | |
| message: format!( | |
| "WORST-CASE noise ({:.1}) is dangerously close to budget ({:.1}). Rare failures may occur.", | |
| noise_budget_max, decryption_budget | |
| ), | |
| suggested_fix: Some("Increase q or decrease noise to increase margin".to_string()), | |
| }); | |
| } else if noise_budget_rms >= decryption_budget * 0.5 { | |
| diagnostics.push(ParamDiagnostic { | |
| severity: Severity::Warning, | |
| message: format!( | |
| "RMS noise ({:.1}) is {:.0}% of budget ({:.1}). Failure rate ≈{:.2}%. Marginal parameters.", | |
| noise_budget_rms, | |
| 100.0 * noise_budget_rms / decryption_budget, | |
| decryption_budget, | |
| failure_prob * 100.0 | |
| ), | |
| suggested_fix: Some("Increase q by 2x or decrease β by √2".to_string()), | |
| }); | |
| } else { | |
| diagnostics.push(ParamDiagnostic { | |
| severity: Severity::Info, | |
| message: format!( | |
| "Noise budget OK: RMS noise {:.1} is {:.0}% of q/4={:.1}.", | |
| noise_budget_rms, | |
| 100.0 * noise_budget_rms / decryption_budget, | |
| decryption_budget | |
| ), | |
| suggested_fix: None, | |
| }); | |
| } | |
| let estimated_security = estimate_security_bits(n, q, std_per_coeff); | |
| if estimated_security < 80.0 { | |
| diagnostics.push(ParamDiagnostic { | |
| severity: Severity::Critical, | |
| message: format!( | |
| "Parameters provide only ~{:.0} bits of security (need ≥128).", | |
| estimated_security | |
| ), | |
| suggested_fix: Some("Use n≥256 and q≥3329 for any security claim".to_string()), | |
| }); | |
| } | |
| let suggested_beta = (decryption_budget / (n_f * 3.0)).sqrt() * 0.5; | |
| let suggested_q = (noise_budget_max * 8.0).ceil() as u64; | |
| ValidationResult { | |
| n, | |
| q, | |
| beta_or_sigma: noise_param, | |
| is_cbd, | |
| noise_budget_max, | |
| noise_budget_rms, | |
| decryption_budget, | |
| failure_prob, | |
| expected_failures_in_500, | |
| passes, | |
| diagnostics, | |
| suggested_beta, | |
| suggested_q, | |
| } | |
| } | |
| } | |
| fn gaussian_q_function(z: f64) -> f64 { | |
| if z > 10.0 { return 0.0; } | |
| if z < 0.0 { return 1.0; } | |
| let t = 1.0 / (1.0 + 0.2316419 * z); | |
| let poly = t * (0.319381530 | |
| + t * (-0.356563782 | |
| + t * (1.781477937 | |
| + t * (-1.821255978 | |
| + t * 1.330274429)))); | |
| let phi = (-z * z / 2.0).exp() / (2.0 * std::f64::consts::PI).sqrt(); | |
| phi * poly | |
| } | |
| fn estimate_security_bits(n: usize, q: u64, sigma: f64) -> f64 { | |
| let n_f = n as f64; | |
| let q_f = q as f64; | |
| let alpha = sigma / q_f; | |
| if alpha <= 0.0 { return 0.0; } | |
| 0.265 * n_f * (-alpha.log2()) | |
| } | |