diff options
Diffstat (limited to 'src/bin/rbw')
-rw-r--r-- | src/bin/rbw/commands.rs | 113 | ||||
-rw-r--r-- | src/bin/rbw/main.rs | 8 |
2 files changed, 88 insertions, 33 deletions
diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs index 3329f76..6d36eb3 100644 --- a/src/bin/rbw/commands.rs +++ b/src/bin/rbw/commands.rs @@ -1018,7 +1018,7 @@ pub fn get( } pub fn code( - name: &str, + needle: &Needle, user: Option<&str>, folder: Option<&str>, clipboard: bool, @@ -1030,12 +1030,11 @@ pub fn code( let desc = format!( "{}{}", user.map_or_else(String::new, |s| format!("{s}@")), - name + needle ); - let (_, decrypted) = - find_entry(&db, &Needle::Name(name.to_string()), user, folder) - .with_context(|| format!("couldn't find entry for '{desc}'"))?; + let (_, decrypted) = find_entry(&db, needle, user, folder) + .with_context(|| format!("couldn't find entry for '{desc}'"))?; if let DecryptedData::Login { totp, .. } = decrypted.data { if let Some(totp) = totp { @@ -1943,8 +1942,23 @@ 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 +1971,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 => totp_lite::DEFAULT_STEP, + } + }) } 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: totp_lite::DEFAULT_STEP, + }) + } } 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 { diff --git a/src/bin/rbw/main.rs b/src/bin/rbw/main.rs index eefa52a..2fb96bf 100644 --- a/src/bin/rbw/main.rs +++ b/src/bin/rbw/main.rs @@ -92,8 +92,8 @@ enum Opt { visible_alias = "totp" )] Code { - #[arg(help = "Name or UUID of the entry to display")] - name: String, + #[arg(help = "Name, URI or UUID of the entry to display", value_parser = commands::parse_needle)] + needle: commands::Needle, #[arg(help = "Username of the entry to display")] user: Option<String>, #[arg(long, help = "Folder name to search in")] @@ -340,12 +340,12 @@ fn main() { *clipboard, ), Opt::Code { - name, + needle, user, folder, clipboard, } => commands::code( - name, + needle, user.as_deref(), folder.as_deref(), *clipboard, |