diff --git a/Cargo.lock b/Cargo.lock index 812dfdc..639beb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,6 +420,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "core-foundation" version = "0.9.4" @@ -1799,17 +1805,15 @@ dependencies = [ "anyhow", "askama", "axum", - "base32", "clap", "k8s-openapi", "kube", "serde", "serde_json", "tokio", - "totp-lite", + "totp-rs", "tracing", "tracing-subscriber", - "url", ] [[package]] @@ -2176,15 +2180,18 @@ dependencies = [ ] [[package]] -name = "totp-lite" -version = "2.0.1" +name = "totp-rs" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8e43134db17199f7f721803383ac5854edd0d3d523cc34dba321d6acfbe76c3" +checksum = "f124352108f58ef88299e909f6e9470f1cdc8d2a1397963901b4a6366206bf72" dependencies = [ - "digest", + "base32", + "constant_time_eq", "hmac", "sha1", "sha2", + "url", + "urlencoding", ] [[package]] @@ -2363,6 +2370,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf8_iter" version = "1.0.4" diff --git a/Cargo.toml b/Cargo.toml index e31b739..d71e8d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,4 @@ clap = { version = "4.5", features = ["derive"] } tracing = "0.1" tracing-subscriber = "0.3" anyhow = "1.0" -totp-lite = "2.0" -url = "2.5" -base32 = "0.5" +totp-rs = { version = "5.6", features = ["otpauth"] } diff --git a/src/main.rs b/src/main.rs index e72d4a5..c9db048 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,11 +12,9 @@ use k8s_openapi::api::core::v1::Secret; use kube::{Api, Client}; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use std::time::{SystemTime, UNIX_EPOCH}; -use totp_lite::{totp, Sha1}; +use totp_rs::TOTP; use tracing::{error, info}; use tracing_subscriber; -use url::Url; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -135,33 +133,23 @@ async fn health_handler() -> impl IntoResponse { } fn generate_totp_code(otpauth_url: &str) -> Option { - let url = Url::parse(otpauth_url).ok()?; - - if url.scheme() != "otpauth" || url.host_str() != Some("totp") { - return None; - } - - let mut secret = None; - let mut period = 30u64; - - for (key, value) in url.query_pairs() { - match key.as_ref() { - "secret" => secret = Some(value.to_string()), - "period" => period = value.parse().unwrap_or(30), - _ => {} + // Try to parse the otpauth URL directly using totp-rs + match TOTP::from_url(otpauth_url) { + Ok(totp) => { + // Generate the current TOTP code + match totp.generate_current() { + Ok(code) => Some(code), + Err(e) => { + error!("Failed to generate TOTP code: {}", e); + None + } + } + } + Err(e) => { + error!("Failed to parse TOTP URL: {}", e); + None } } - - let secret = secret?; - let decoded = base32::decode(base32::Alphabet::Rfc4648 { padding: false }, &secret)?; - - let time = SystemTime::now() - .duration_since(UNIX_EPOCH) - .ok()? - .as_secs() / period; - - let code = totp::(&decoded, time); - Some(code) } async fn secret_handler(