aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--src/bin/rbw/commands.rs42
3 files changed, 31 insertions, 13 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 54062ad..f3ff782 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1266,6 +1266,7 @@ dependencies = [
"term_size",
"textwrap 0.12.1",
"tokio",
+ "url",
"uuid",
"zeroize",
]
diff --git a/Cargo.toml b/Cargo.toml
index fc50ebf..d0c1d5a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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)
))
}