diff options
author | Jesse Luehrs <doy@tozt.net> | 2020-04-06 07:20:11 -0400 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2020-04-06 07:20:11 -0400 |
commit | 1c40a0f9ff0ced652ee8b74f2333000ca47a0692 (patch) | |
tree | a0be3e77060d7f267596d70e4ce14b4604f9bb25 | |
parent | bc18bca5c67b4a678a31198877e39d57d97b1e0c (diff) | |
download | rbw-1c40a0f9ff0ced652ee8b74f2333000ca47a0692.tar.gz rbw-1c40a0f9ff0ced652ee8b74f2333000ca47a0692.zip |
a bit more cleanup
-rw-r--r-- | src/actions.rs | 37 | ||||
-rw-r--r-- | src/bin/agent.rs | 20 | ||||
-rw-r--r-- | src/error.rs | 22 | ||||
-rw-r--r-- | src/pinentry.rs | 55 |
4 files changed, 87 insertions, 47 deletions
diff --git a/src/actions.rs b/src/actions.rs index d7f3103..0998abc 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -1,20 +1,21 @@ -// TODO api needs to be async +use crate::prelude::*; -pub async fn login(email: &str, password: &str) -> (String, u32, String) { +pub async fn login( + email: &str, + password: &str, +) -> Result<(String, u32, String)> { let client = crate::api::Client::new_self_hosted("https://bitwarden.tozt.net"); - let iterations = client.prelogin(&email).await.unwrap(); + let iterations = client.prelogin(&email).await?; let identity = - crate::identity::Identity::new(&email, &password, iterations) - .unwrap(); + crate::identity::Identity::new(&email, &password, iterations)?; let (access_token, _refresh_token, protected_key) = client .login(&identity.email, &identity.master_password_hash) - .await - .unwrap(); + .await?; - (access_token, iterations, protected_key) + Ok((access_token, iterations, protected_key)) } pub async fn unlock( @@ -22,25 +23,25 @@ pub async fn unlock( password: &str, iterations: u32, protected_key: String, -) -> (Vec<u8>, Vec<u8>) { +) -> Result<(Vec<u8>, Vec<u8>)> { let identity = - crate::identity::Identity::new(&email, &password, iterations) - .unwrap(); + crate::identity::Identity::new(&email, &password, iterations)?; let protected_key = - crate::cipherstring::CipherString::new(&protected_key).unwrap(); - let master_key = protected_key - .decrypt(&identity.enc_key, &identity.mac_key) - .unwrap(); + crate::cipherstring::CipherString::new(&protected_key)?; + let master_key = + protected_key.decrypt(&identity.enc_key, &identity.mac_key)?; let enc_key = &master_key[0..32]; let mac_key = &master_key[32..64]; - (enc_key.to_vec(), mac_key.to_vec()) + Ok((enc_key.to_vec(), mac_key.to_vec())) } -pub async fn sync(access_token: &str) -> (String, Vec<crate::api::Cipher>) { +pub async fn sync( + access_token: &str, +) -> Result<(String, Vec<crate::api::Cipher>)> { let client = crate::api::Client::new_self_hosted("https://bitwarden.tozt.net"); - client.sync(access_token).await.unwrap() + client.sync(access_token).await } diff --git a/src/bin/agent.rs b/src/bin/agent.rs index 5a97448..dedfe68 100644 --- a/src/bin/agent.rs +++ b/src/bin/agent.rs @@ -62,14 +62,17 @@ async fn login( ) { let mut state = state.write().await; let email = "bitwarden@tozt.net"; // XXX read from config - let password = rbw::pinentry::pinentry("prompt", "desc", tty).await; + let password = rbw::pinentry::pinentry("prompt", "desc", tty) + .await + .unwrap(); let (access_token, iterations, protected_key) = - rbw::actions::login(email, &password).await; + rbw::actions::login(email, &password).await.unwrap(); state.access_token = Some(access_token); state.iterations = Some(iterations); let (enc_key, mac_key) = rbw::actions::unlock(email, &password, iterations, protected_key) - .await; + .await + .unwrap(); state.priv_key = Some((enc_key, mac_key)); } @@ -86,14 +89,17 @@ async fn unlock( ) { let mut state = state.write().await; let email = "bitwarden@tozt.net"; // XXX read from config - let password = rbw::pinentry::pinentry("prompt", "desc", tty).await; + let password = rbw::pinentry::pinentry("prompt", "desc", tty) + .await + .unwrap(); let (enc_key, mac_key) = rbw::actions::unlock( email, &password, state.iterations.unwrap(), state.protected_key.as_ref().unwrap().to_string(), ) - .await; + .await + .unwrap(); state.priv_key = Some((enc_key, mac_key)); } @@ -101,7 +107,9 @@ async fn sync(state: std::sync::Arc<tokio::sync::RwLock<State>>) { ensure_login(state.clone()).await; let mut state = state.write().await; let (protected_key, ciphers) = - rbw::actions::sync(state.access_token.as_ref().unwrap()).await; + rbw::actions::sync(state.access_token.as_ref().unwrap()) + .await + .unwrap(); state.protected_key = Some(protected_key); println!("{}", serde_json::to_string(&ciphers).unwrap()); state.ciphers = ciphers; diff --git a/src/error.rs b/src/error.rs index 8ebbfcd..8947fd0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,19 @@ pub enum Error { #[snafu(display("failed to decrypt: {}", source))] Decrypt { source: block_modes::BlockModeError }, + #[snafu(display("failed to parse pinentry output ({:?})", out,))] + FailedToParsePinentry { out: Vec<u8> }, + + #[snafu(display( + "failed to parse pinentry output ({:?}): {}", + out, + source + ))] + FailedToParsePinentryUtf8 { + out: Vec<u8>, + source: std::string::FromUtf8Error, + }, + // no Error impl // #[snafu(display("failed to expand with hkdf: {}", source))] // HkdfExpand { source: hkdf::InvalidLength }, @@ -36,8 +49,17 @@ pub enum Error { #[snafu(display("invalid mac key"))] InvalidMacKey, + #[snafu(display("error waiting for pinentry to exit: {}", source))] + ProcessWaitOutput { source: tokio::io::Error }, + #[snafu(display("error making api request: {}", source))] Reqwest { source: reqwest::Error }, + + #[snafu(display("error spawning pinentry: {}", source))] + Spawn { source: tokio::io::Error }, + + #[snafu(display("error writing to pinentry stdin: {}", source))] + WriteStdin { source: tokio::io::Error }, } pub type Result<T> = std::result::Result<T, Error>; diff --git a/src/pinentry.rs b/src/pinentry.rs index 610f52d..8a25759 100644 --- a/src/pinentry.rs +++ b/src/pinentry.rs @@ -1,7 +1,12 @@ -use tokio::io::{AsyncBufReadExt as _, AsyncWriteExt as _}; +use crate::prelude::*; -// TODO result -pub async fn pinentry(prompt: &str, desc: &str, tty: Option<&str>) -> String { +use tokio::io::AsyncWriteExt as _; + +pub async fn pinentry( + prompt: &str, + desc: &str, + tty: Option<&str>, +) -> Result<String> { let mut opts = tokio::process::Command::new("pinentry"); let opts = opts .stdin(std::process::Stdio::piped()) @@ -11,39 +16,43 @@ pub async fn pinentry(prompt: &str, desc: &str, tty: Option<&str>) -> String { } else { opts }; - let mut child = opts.spawn().unwrap(); + let mut child = opts.spawn().context(crate::error::Spawn)?; { let stdin = child.stdin.as_mut().unwrap(); - let mut stdout = - tokio::io::BufReader::new(child.stdout.as_mut().unwrap()); - let mut buf = String::new(); - - stdin.write_all(b"SETTITLE rbw\n").await.unwrap(); - stdout.read_line(&mut buf).await.unwrap(); stdin + .write_all(b"SETTITLE rbw\n") + .await + .context(crate::error::WriteStdin)?; + stdin .write_all(format!("SETPROMPT {}\n", prompt).as_bytes()) .await - .unwrap(); - stdout.read_line(&mut buf).await.unwrap(); - + .context(crate::error::WriteStdin)?; stdin .write_all(format!("SETDESC {}\n", desc).as_bytes()) .await - .unwrap(); - stdout.read_line(&mut buf).await.unwrap(); - - stdin.write_all(b"GETPIN\n").await.unwrap(); + .context(crate::error::WriteStdin)?; + stdin + .write_all(b"GETPIN\n") + .await + .context(crate::error::WriteStdin)?; } - let res = - String::from_utf8(child.wait_with_output().await.unwrap().stdout) - .unwrap(); - for line in res.lines() { + + let out = child + .wait_with_output() + .await + .context(crate::error::ProcessWaitOutput)? + .stdout; + let out_str = String::from_utf8(out.clone()).context( + crate::error::FailedToParsePinentryUtf8 { out: out.clone() }, + )?; + for line in out_str.lines() { if line.starts_with("OK") { continue; } else if line.starts_with("D ") { - return line[2..line.len()].to_string(); + return Ok(line[2..line.len()].to_string()); } } - panic!("failed to parse pinentry output: {:?}", res) + + Err(Error::FailedToParsePinentry { out }) } |