aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2020-07-25 03:53:02 -0400
committerJesse Luehrs <doy@tozt.net>2020-07-25 04:10:20 -0400
commit56e9b720bade5dcdc6505952c1bf81bdeca26bcd (patch)
treee85f8e09e258a1a86b6c905c6c8772d9c27fef24 /src
parentc5d01a97ae4fda0871c8fd49777be325376ba5df (diff)
downloadrbw-56e9b720bade5dcdc6505952c1bf81bdeca26bcd.tar.gz
rbw-56e9b720bade5dcdc6505952c1bf81bdeca26bcd.zip
also display the totp code
Diffstat (limited to 'src')
-rw-r--r--src/bin/rbw/commands.rs53
-rw-r--r--src/bin/rbw/main.rs14
2 files changed, 67 insertions, 0 deletions
diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs
index 3180cc9..e5858b6 100644
--- a/src/bin/rbw/commands.rs
+++ b/src/bin/rbw/commands.rs
@@ -545,6 +545,40 @@ pub fn get(
Ok(())
}
+pub fn code(
+ name: &str,
+ user: Option<&str>,
+ folder: Option<&str>,
+) -> anyhow::Result<()> {
+ unlock()?;
+
+ let db = load_db()?;
+
+ let desc = format!(
+ "{}{}",
+ user.map(|s| format!("{}@", s))
+ .unwrap_or_else(|| "".to_string()),
+ name
+ );
+
+ let (_, decrypted) = find_entry(&db, name, user, folder)
+ .with_context(|| format!("couldn't find entry for '{}'", desc))?;
+
+ if let DecryptedData::Login { totp, .. } = decrypted.data {
+ if let Some(totp) = totp {
+ println!("{}", generate_totp(&totp)?)
+ } else {
+ return Err(anyhow::anyhow!(
+ "entry does not contain a totp secret"
+ ));
+ }
+ } else {
+ return Err(anyhow::anyhow!("not a login entry"));
+ }
+
+ Ok(())
+}
+
pub fn add(
name: &str,
username: Option<&str>,
@@ -1392,6 +1426,25 @@ fn remove_db() -> anyhow::Result<()> {
}
}
+fn generate_totp(secret: &str) -> anyhow::Result<String> {
+ Ok(format!(
+ "{}",
+ 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,
+ )
+ ))
+}
+
#[cfg(test)]
mod test {
use super::*;
diff --git a/src/bin/rbw/main.rs b/src/bin/rbw/main.rs
index 258fd04..f1903e7 100644
--- a/src/bin/rbw/main.rs
+++ b/src/bin/rbw/main.rs
@@ -56,6 +56,16 @@ enum Opt {
full: bool,
},
+ #[structopt(about = "Display the authenticator code for a given entry")]
+ Code {
+ #[structopt(help = "Name or UUID of the entry to display")]
+ name: String,
+ #[structopt(help = "Username of the entry to display")]
+ user: Option<String>,
+ #[structopt(long, help = "Folder name to search in")]
+ folder: Option<String>,
+ },
+
#[structopt(
about = "Add a new password to the database",
long_about = "Add a new password to the database\n\n\
@@ -200,6 +210,7 @@ impl Opt {
Self::Sync => "sync".to_string(),
Self::List { .. } => "list".to_string(),
Self::Get { .. } => "get".to_string(),
+ Self::Code { .. } => "code".to_string(),
Self::Add { .. } => "add".to_string(),
Self::Generate { .. } => "generate".to_string(),
Self::Edit { .. } => "edit".to_string(),
@@ -272,6 +283,9 @@ fn main(opt: Opt) {
folder,
full,
} => commands::get(&name, user.as_deref(), folder.as_deref(), *full),
+ Opt::Code { name, user, folder } => {
+ commands::code(&name, user.as_deref(), folder.as_deref())
+ }
Opt::Add {
name,
user,