fix passkey cli argument being printed in plaintext with debug logging (#229)
fixes #228
This commit is contained in:
parent
ac7717bde8
commit
a062e718d7
7 changed files with 39 additions and 23 deletions
|
@ -2,6 +2,7 @@ use crate::accountmanager::legacy::SdaManifest;
|
|||
pub use crate::encryption::EntryEncryptionParams;
|
||||
use crate::encryption::EntryEncryptor;
|
||||
use log::*;
|
||||
use secrecy::{ExposeSecret, SecretString};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Read, Write};
|
||||
|
@ -21,7 +22,7 @@ pub struct AccountManager {
|
|||
manifest: Manifest,
|
||||
accounts: HashMap<String, Arc<Mutex<SteamGuardAccount>>>,
|
||||
folder: String,
|
||||
passkey: Option<String>,
|
||||
passkey: Option<SecretString>,
|
||||
}
|
||||
|
||||
impl AccountManager {
|
||||
|
@ -73,9 +74,9 @@ impl AccountManager {
|
|||
}
|
||||
|
||||
/// Tells the manager to keep track of the encryption passkey, and use it for encryption when loading or saving accounts.
|
||||
pub fn submit_passkey(&mut self, passkey: Option<String>) {
|
||||
pub fn submit_passkey(&mut self, passkey: Option<SecretString>) {
|
||||
if let Some(p) = passkey.as_ref() {
|
||||
if p.is_empty() {
|
||||
if p.expose_secret().is_empty() {
|
||||
panic!("Encryption passkey cannot be empty");
|
||||
}
|
||||
}
|
||||
|
@ -199,9 +200,11 @@ impl AccountManager {
|
|||
);
|
||||
|
||||
let final_buffer: Vec<u8> = match (&self.passkey, entry.encryption.as_ref()) {
|
||||
(Some(passkey), Some(params)) => {
|
||||
crate::encryption::LegacySdaCompatible::encrypt(passkey, params, serialized)?
|
||||
}
|
||||
(Some(passkey), Some(params)) => crate::encryption::LegacySdaCompatible::encrypt(
|
||||
passkey.expose_secret(),
|
||||
params,
|
||||
serialized,
|
||||
)?,
|
||||
(None, Some(_)) => {
|
||||
bail!("maFiles are encrypted, but no passkey was provided.");
|
||||
}
|
||||
|
@ -338,7 +341,7 @@ trait EntryLoader<T> {
|
|||
fn load(
|
||||
&self,
|
||||
path: &Path,
|
||||
passkey: Option<&String>,
|
||||
passkey: Option<&SecretString>,
|
||||
encryption_params: Option<&EntryEncryptionParams>,
|
||||
) -> anyhow::Result<T, ManifestAccountLoadError>;
|
||||
}
|
||||
|
@ -347,7 +350,7 @@ impl EntryLoader<SteamGuardAccount> for ManifestEntry {
|
|||
fn load(
|
||||
&self,
|
||||
path: &Path,
|
||||
passkey: Option<&String>,
|
||||
passkey: Option<&SecretString>,
|
||||
encryption_params: Option<&EntryEncryptionParams>,
|
||||
) -> anyhow::Result<SteamGuardAccount, ManifestAccountLoadError> {
|
||||
debug!("loading entry: {:?}", path);
|
||||
|
@ -357,8 +360,11 @@ impl EntryLoader<SteamGuardAccount> for ManifestEntry {
|
|||
(Some(passkey), Some(params)) => {
|
||||
let mut ciphertext: Vec<u8> = vec![];
|
||||
reader.read_to_end(&mut ciphertext)?;
|
||||
let plaintext =
|
||||
crate::encryption::LegacySdaCompatible::decrypt(passkey, params, ciphertext)?;
|
||||
let plaintext = crate::encryption::LegacySdaCompatible::decrypt(
|
||||
passkey.expose_secret(),
|
||||
params,
|
||||
ciphertext,
|
||||
)?;
|
||||
if plaintext[0] != b'{' && plaintext[plaintext.len() - 1] != b'}' {
|
||||
return Err(ManifestAccountLoadError::IncorrectPasskey);
|
||||
}
|
||||
|
@ -494,7 +500,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_should_save_and_load_manifest_encrypted() -> anyhow::Result<()> {
|
||||
let passkey = Some("password".into());
|
||||
let passkey = Some(SecretString::new("password".into()));
|
||||
let tmp_dir = TempDir::new("steamguard-cli-test")?;
|
||||
let manifest_path = tmp_dir.path().join("manifest.json");
|
||||
let mut manager = AccountManager::new(manifest_path.as_path());
|
||||
|
@ -559,7 +565,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_should_save_and_load_manifest_encrypted_longer() -> anyhow::Result<()> {
|
||||
let passkey = Some("password".into());
|
||||
let passkey = Some(SecretString::new("password".into()));
|
||||
let tmp_dir = TempDir::new("steamguard-cli-test")?;
|
||||
let manifest_path = tmp_dir.path().join("manifest.json");
|
||||
let mut manager = AccountManager::new(manifest_path.as_path());
|
||||
|
|
|
@ -72,7 +72,7 @@ impl EntryLoader<SdaAccount> for SdaManifestEntry {
|
|||
fn load(
|
||||
&self,
|
||||
path: &Path,
|
||||
passkey: Option<&String>,
|
||||
passkey: Option<&SecretString>,
|
||||
encryption_params: Option<&EntryEncryptionParams>,
|
||||
) -> anyhow::Result<SdaAccount, ManifestAccountLoadError> {
|
||||
debug!("loading entry: {:?}", path);
|
||||
|
@ -82,8 +82,11 @@ impl EntryLoader<SdaAccount> for SdaManifestEntry {
|
|||
(Some(passkey), Some(params)) => {
|
||||
let mut ciphertext: Vec<u8> = vec![];
|
||||
reader.read_to_end(&mut ciphertext)?;
|
||||
let plaintext =
|
||||
crate::encryption::LegacySdaCompatible::decrypt(passkey, params, ciphertext)?;
|
||||
let plaintext = crate::encryption::LegacySdaCompatible::decrypt(
|
||||
passkey.expose_secret(),
|
||||
params,
|
||||
ciphertext,
|
||||
)?;
|
||||
if plaintext[0] != b'{' && plaintext[plaintext.len() - 1] != b'}' {
|
||||
return Err(ManifestAccountLoadError::IncorrectPasskey);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::{fs::File, io::Read, path::Path};
|
||||
|
||||
use log::debug;
|
||||
use secrecy::SecretString;
|
||||
use serde::{de::Error, Deserialize};
|
||||
use steamguard::SteamGuardAccount;
|
||||
use thiserror::Error;
|
||||
|
@ -13,7 +14,7 @@ use super::{
|
|||
|
||||
pub(crate) fn load_and_migrate(
|
||||
manifest_path: &Path,
|
||||
passkey: Option<&String>,
|
||||
passkey: Option<&SecretString>,
|
||||
) -> Result<(Manifest, Vec<SteamGuardAccount>), MigrationError> {
|
||||
backup_file(manifest_path)?;
|
||||
let parent = manifest_path.parent().unwrap();
|
||||
|
@ -32,7 +33,7 @@ pub(crate) fn load_and_migrate(
|
|||
|
||||
fn do_migrate(
|
||||
manifest_path: &Path,
|
||||
passkey: Option<&String>,
|
||||
passkey: Option<&SecretString>,
|
||||
) -> Result<(Manifest, Vec<SteamGuardAccount>), MigrationError> {
|
||||
let mut file = File::open(manifest_path)?;
|
||||
let mut buffer = String::new();
|
||||
|
@ -117,7 +118,7 @@ impl MigratingManifest {
|
|||
pub fn load_all_accounts(
|
||||
&self,
|
||||
folder: &Path,
|
||||
passkey: Option<&String>,
|
||||
passkey: Option<&SecretString>,
|
||||
) -> anyhow::Result<Vec<MigratingAccount>> {
|
||||
debug!("loading all accounts for migration");
|
||||
let accounts = match self {
|
||||
|
@ -271,7 +272,7 @@ mod tests {
|
|||
#[derive(Debug)]
|
||||
struct Test {
|
||||
manifest: &'static str,
|
||||
passkey: Option<String>,
|
||||
passkey: Option<SecretString>,
|
||||
}
|
||||
let cases = vec![
|
||||
Test {
|
||||
|
@ -280,7 +281,7 @@ mod tests {
|
|||
},
|
||||
Test {
|
||||
manifest: "src/fixtures/maFiles/compat/1-account-encrypted/manifest.json",
|
||||
passkey: Some("password".into()),
|
||||
passkey: Some(SecretString::new("password".into())),
|
||||
},
|
||||
Test {
|
||||
manifest: "src/fixtures/maFiles/compat/2-account/manifest.json",
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::sync::{Arc, Mutex};
|
|||
|
||||
use clap::{clap_derive::ArgEnum, Parser};
|
||||
use clap_complete::Shell;
|
||||
use secrecy::SecretString;
|
||||
use std::str::FromStr;
|
||||
use steamguard::SteamGuardAccount;
|
||||
|
||||
|
@ -101,7 +102,7 @@ pub(crate) struct GlobalArgs {
|
|||
env = "STEAMGUARD_CLI_PASSKEY",
|
||||
help = "Specify your encryption passkey."
|
||||
)]
|
||||
pub passkey: Option<String>,
|
||||
pub passkey: Option<SecretString>,
|
||||
#[clap(short, long, arg_enum, default_value_t=Verbosity::Info, help = "Set the log level. Be warned, trace is capable of printing sensitive data.")]
|
||||
pub verbosity: Verbosity,
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ fn load_accounts_with_prompts(manager: &mut AccountManager) -> anyhow::Result<()
|
|||
error!("Incorrect passkey");
|
||||
}
|
||||
let passkey = rpassword::prompt_password_stdout("Enter encryption passkey: ").ok();
|
||||
let passkey = passkey.map(SecretString::new);
|
||||
manager.submit_passkey(passkey);
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
|
@ -27,6 +27,7 @@ impl ManifestCommand for EncryptCommand {
|
|||
}
|
||||
error!("Passkeys do not match, try again.");
|
||||
}
|
||||
let passkey = passkey.map(SecretString::new);
|
||||
manager.submit_passkey(passkey);
|
||||
}
|
||||
manager.load_accounts()?;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
extern crate rpassword;
|
||||
use clap::Parser;
|
||||
use log::*;
|
||||
use secrecy::SecretString;
|
||||
use std::{
|
||||
io::Write,
|
||||
path::Path,
|
||||
|
@ -164,7 +165,8 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
|
|||
if manager.has_passkey() {
|
||||
error!("Incorrect passkey");
|
||||
}
|
||||
passkey = rpassword::prompt_password_stderr("Enter encryption passkey: ").ok();
|
||||
let raw = rpassword::prompt_password_stdout("Enter encryption passkey: ")?;
|
||||
passkey = Some(SecretString::new(raw));
|
||||
manager.submit_passkey(passkey);
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -193,7 +195,8 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
|
|||
if manager.has_passkey() {
|
||||
error!("Incorrect passkey");
|
||||
}
|
||||
passkey = rpassword::prompt_password_stdout("Enter encryption passkey: ").ok();
|
||||
let raw = rpassword::prompt_password_stdout("Enter encryption passkey: ")?;
|
||||
passkey = Some(SecretString::new(raw));
|
||||
manager.submit_passkey(passkey);
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
Loading…
Reference in a new issue