diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/bin/rbw/commands.rs | 42 |
3 files changed, 31 insertions, 13 deletions
@@ -1266,6 +1266,7 @@ dependencies = [ "term_size", "textwrap 0.12.1", "tokio", + "url", "uuid", "zeroize", ] @@ -44,6 +44,7 @@ tempfile = "3.1" term_size = "0.3" textwrap = "0.12" tokio = { version = "0.2", features = ["full"] } +url = "2.1" uuid = { version = "0.8", features = ["v4"] } zeroize = "1.1" diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs index bd1b1c2..3fa30f4 100644 --- a/src/bin/rbw/commands.rs +++ b/src/bin/rbw/commands.rs @@ -1433,22 +1433,38 @@ 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) { + if u.scheme() != "otpauth" { + return Err(anyhow::anyhow!( + "totp secret url must have otpauth scheme" + )); + } + if u.host_str() != Some("totp") { + return Err(anyhow::anyhow!( + "totp secret url must have totp host" + )); + } + 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() + } else { + secret.to_string() + }; + base32::decode(base32::Alphabet::RFC4648 { padding: false }, &secret_str) + .ok_or_else(|| anyhow::anyhow!("totp secret was not valid base32")) +} + fn generate_totp(secret: &str) -> anyhow::Result<String> { + let key = parse_totp_secret(secret)?; Ok(format!( "{:06}", - oath::totp_raw_now( - &base32::decode( - base32::Alphabet::RFC4648 { padding: false }, - secret - ) - .ok_or_else(|| anyhow::anyhow!( - "totp secret was not valid base32" - ))?, - 6, - 0, - 30, - &oath::HashType::SHA1, - ) + oath::totp_raw_now(&key, 6, 0, 30, &oath::HashType::SHA1) )) } |