diff options
author | Jonas <git@03j.de> | 2024-05-07 23:30:47 +0200 |
---|---|---|
committer | Jonas <git@03j.de> | 2024-05-08 01:00:05 +0200 |
commit | b9fa3342976fe639ef3c47173042ae0e7d4ea03f (patch) | |
tree | bc6a5fe288b093e0d2d774f66ce97b5e67475fe2 /src/bin | |
parent | df5c9f1d2c869925cbfdddc8176f7d000e27a237 (diff) | |
download | rbw-b9fa3342976fe639ef3c47173042ae0e7d4ea03f.tar.gz rbw-b9fa3342976fe639ef3c47173042ae0e7d4ea03f.zip |
add sha256 and sha512 support for totp
Diffstat (limited to 'src/bin')
-rw-r--r-- | src/bin/rbw/commands.rs | 102 |
1 files changed, 78 insertions, 24 deletions
diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs index 3329f76..93c216b 100644 --- a/src/bin/rbw/commands.rs +++ b/src/bin/rbw/commands.rs @@ -1943,8 +1943,21 @@ fn remove_db() -> anyhow::Result<()> { ) } -fn parse_totp_secret(secret: &str) -> anyhow::Result<Vec<u8>> { - let secret_str = if let Ok(u) = url::Url::parse(secret) { +struct TotpParams { + secret: Vec<u8>, + algorithm: String, + digits: u32, + period: u64, +} +fn decode_totp_secret(secret: &str) -> anyhow::Result<Vec<u8>> { + base32::decode( + base32::Alphabet::RFC4648 { padding: false }, + &secret.replace(' ', ""), + ) + .ok_or_else(|| anyhow::anyhow!("totp secret was not valid base32")) +} +fn parse_totp_secret(secret: &str) -> anyhow::Result<TotpParams> { + if let Ok(u) = url::Url::parse(secret) { if u.scheme() != "otpauth" { return Err(anyhow::anyhow!( "totp secret url must have otpauth scheme" @@ -1957,32 +1970,73 @@ fn parse_totp_secret(secret: &str) -> anyhow::Result<Vec<u8>> { } let query: std::collections::HashMap<_, _> = u.query_pairs().collect(); - query - .get("secret") - .ok_or_else(|| { - anyhow::anyhow!("totp secret url must have secret") - })? - .to_string() + Ok(TotpParams { + secret: decode_totp_secret(query + .get("secret") + .ok_or_else(|| { + anyhow::anyhow!("totp secret url must have secret") + })?)?, + algorithm:query.get("algorithm").map_or_else(||{String::from("SHA1")},|alg|{alg.to_string()} ), + digits: match query.get("digits") { + Some(dig) => { + dig.parse::<u32>().map_err(|_|{ + anyhow::anyhow!("digits parameter in totp url must be a valid integer.") + })? + } + None => 6, + }, + period: match query.get("period") { + Some(dig) => { + dig.parse::<u64>().map_err(|_|{ + anyhow::anyhow!("period parameter in totp url must be a valid integer.") + })? + } + None => 30, + } + }) } else { - secret.to_string() - }; - base32::decode( - base32::Alphabet::RFC4648 { padding: false }, - &secret_str.replace(' ', ""), - ) - .ok_or_else(|| anyhow::anyhow!("totp secret was not valid base32")) + Ok(TotpParams { + secret: decode_totp_secret(secret)?, + algorithm: String::from("SHA1"), + digits: 6, + period: 30, + }) + } } fn generate_totp(secret: &str) -> anyhow::Result<String> { - let key = parse_totp_secret(secret)?; - Ok(totp_lite::totp_custom::<totp_lite::Sha1>( - totp_lite::DEFAULT_STEP, - 6, - &key, - std::time::SystemTime::now() - .duration_since(std::time::SystemTime::UNIX_EPOCH)? - .as_secs(), - )) + let totp_params = parse_totp_secret(secret)?; + let alg = totp_params.algorithm.as_str(); + match alg { + "SHA1" => Ok(totp_lite::totp_custom::<totp_lite::Sha1>( + totp_params.period, + totp_params.digits, + &totp_params.secret, + std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH)? + .as_secs(), + )), + "SHA256" => Ok(totp_lite::totp_custom::<totp_lite::Sha256>( + totp_params.period, + totp_params.digits, + &totp_params.secret, + std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH)? + .as_secs(), + )), + "SHA512" => Ok(totp_lite::totp_custom::<totp_lite::Sha512>( + totp_params.period, + totp_params.digits, + &totp_params.secret, + std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH)? + .as_secs(), + )), + _ => Err(anyhow::anyhow!(format!( + "{} is not a valid totp algorithm", + alg + ))), + } } fn display_field(name: &str, field: Option<&str>, clipboard: bool) -> bool { |