diff --git a/yazi-config/src/vfs/service.rs b/yazi-config/src/vfs/service.rs index 8e77d145..ffc6f582 100644 --- a/yazi-config/src/vfs/service.rs +++ b/yazi-config/src/vfs/service.rs @@ -39,9 +39,9 @@ pub struct ServiceSftp { pub key_file: PathBuf, pub key_passphrase: Option, #[serde(default)] - pub cert_file: PathBuf, + pub cert_file: PathBuf, #[serde(default)] - pub skip_cert_validation: bool, + pub no_cert_verify: bool, #[serde(default)] pub identity_agent: PathBuf, } diff --git a/yazi-shared/src/time.rs b/yazi-shared/src/time.rs index 8d7b1f38..59d72c61 100644 --- a/yazi-shared/src/time.rs +++ b/yazi-shared/src/time.rs @@ -1,6 +1,35 @@ -use std::time::{SystemTime, UNIX_EPOCH}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; #[inline] pub fn timestamp_us() -> u64 { SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_micros() as _ } + +pub fn format_duration(duration: Duration) -> String { + // go till days precision, but only show the non-zero parts + let mut secs = duration.as_secs(); + let days = secs / 86_400; + secs %= 86_400; + let hours = secs / 3_600; + secs %= 3_600; + let minutes = secs / 60; + secs %= 60; + let mut parts = Vec::with_capacity(4); + if days > 0 { + parts.push(format!("{}d", days)); + } + if hours > 0 || days > 0 { + parts.push(format!("{:0>2}h", hours)); + } + if minutes > 0 || hours > 0 || days > 0 { + parts.push(format!("{:0>2}m", minutes)); + } + if secs > 0 || minutes > 0 || hours > 0 || days > 0 { + parts.push(format!("{:0>2}s", secs)); + } + if parts.is_empty() { + "0s".to_string() + } else { + parts.join("") + } +} \ No newline at end of file diff --git a/yazi-vfs/src/provider/sftp/conn.rs b/yazi-vfs/src/provider/sftp/conn.rs index 2df060dc..c51e8000 100644 --- a/yazi-vfs/src/provider/sftp/conn.rs +++ b/yazi-vfs/src/provider/sftp/conn.rs @@ -3,6 +3,7 @@ use std::{io, sync::Arc, time::Duration}; use russh::keys::PrivateKeyWithHashAlg; use yazi_config::vfs::ServiceSftp; use yazi_fs::provider::local::Local; +use yazi_shared::{format_duration, timestamp_us}; #[derive(Clone, Copy)] pub(super) struct Conn { @@ -169,18 +170,21 @@ impl Conn { }, )?; - if !self.config.skip_cert_validation { - // TODO: Also validate the certificate cryptographically: https://docs.rs/russh/latest/russh/keys/struct.Certificate.html#method.validate - // however, this would require fetching CAs, which could require additional config and complexity - + if !self.config.no_cert_verify { cert.verify_signature().map_err(|e| { russh::Error::InvalidConfig(format!("Certificate signature verification failed: {e}")) })?; - let unix_timestamp = std::time::UNIX_EPOCH.elapsed().unwrap_or_default().as_secs(); - if !(cert.valid_after() <= unix_timestamp && unix_timestamp <= cert.valid_before()) { - return Err(russh::Error::InvalidConfig( - "Certificate is not valid at this time".to_owned(), - )); + let unix_timestamp = timestamp_us() / 1_000_000; + if unix_timestamp < cert.valid_after() { + return Err(russh::Error::InvalidConfig(format!( + "Certificate will be valid in {}", + format_duration(Duration::from_secs(cert.valid_after() - unix_timestamp)) + ))); + } else if unix_timestamp > cert.valid_before() { + return Err(russh::Error::InvalidConfig(format!( + "Certificate expired {} ago", + format_duration(Duration::from_secs(unix_timestamp - cert.valid_before())) + ))); } }