fix manifest migrations not working for encrypted maFiles (#201)

fixes #200
This commit is contained in:
Carson McManus 2023-06-23 08:40:23 -04:00 committed by GitHub
parent 7950e43f70
commit 8defefb11e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 7 deletions

View file

@ -4,7 +4,9 @@
use std::{fs::File, io::Read, path::Path}; use std::{fs::File, io::Read, path::Path};
use log::debug; use log::debug;
use serde::de::Error;
use steamguard::SteamGuardAccount; use steamguard::SteamGuardAccount;
use thiserror::Error;
use super::{ use super::{
legacy::{SdaAccount, SdaManifest}, legacy::{SdaAccount, SdaManifest},
@ -12,10 +14,10 @@ use super::{
EntryEncryptionParams, EntryLoader, Manifest, EntryEncryptionParams, EntryLoader, Manifest,
}; };
pub fn load_and_migrate( pub(crate) fn load_and_migrate(
manifest_path: &Path, manifest_path: &Path,
passkey: Option<&String>, passkey: Option<&String>,
) -> anyhow::Result<(Manifest, Vec<SteamGuardAccount>)> { ) -> Result<(Manifest, Vec<SteamGuardAccount>), MigrationError> {
backup_file(manifest_path)?; backup_file(manifest_path)?;
let parent = manifest_path.parent().unwrap(); let parent = manifest_path.parent().unwrap();
parent.read_dir()?.for_each(|e| { parent.read_dir()?.for_each(|e| {
@ -34,11 +36,19 @@ pub fn load_and_migrate(
fn do_migrate( fn do_migrate(
manifest_path: &Path, manifest_path: &Path,
passkey: Option<&String>, passkey: Option<&String>,
) -> anyhow::Result<(Manifest, Vec<SteamGuardAccount>)> { ) -> Result<(Manifest, Vec<SteamGuardAccount>), MigrationError> {
let mut file = File::open(manifest_path)?; let mut file = File::open(manifest_path)?;
let mut buffer = String::new(); let mut buffer = String::new();
file.read_to_string(&mut buffer)?; file.read_to_string(&mut buffer)?;
let mut manifest: MigratingManifest = deserialize_manifest(buffer)?; let mut manifest: MigratingManifest = deserialize_manifest(buffer)
.map_err(|err| MigrationError::ManifestDeserializeFailed(err))?;
if manifest.is_encrypted() && passkey.is_none() {
return Err(MigrationError::MissingPasskey);
} else if !manifest.is_encrypted() && passkey.is_some() {
// no custom error because this is an edge case, mostly user error
return Err(MigrationError::UnexpectedError(anyhow::anyhow!("A passkey was provided but the manifest is not encrypted. Aborting migration because it would encrypt the maFiles, and you probably didn't mean to do that.")));
}
let folder = manifest_path.parent().unwrap(); let folder = manifest_path.parent().unwrap();
let mut accounts = manifest.load_all_accounts(folder, passkey)?; let mut accounts = manifest.load_all_accounts(folder, passkey)?;
@ -70,6 +80,18 @@ fn backup_file(path: &Path) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[derive(Debug, Error)]
pub(crate) enum MigrationError {
#[error("Passkey is required to decrypt manifest")]
MissingPasskey,
#[error("Failed to deserialize manifest: {0}")]
ManifestDeserializeFailed(serde_json::Error),
#[error("IO error when upgrading manifest: {0}")]
IoError(#[from] std::io::Error),
#[error("An unexpected error occurred during manifest migration: {0}")]
UnexpectedError(#[from] anyhow::Error),
}
#[derive(Debug)] #[derive(Debug)]
enum MigratingManifest { enum MigratingManifest {
SDA(SdaManifest), SDA(SdaManifest),
@ -98,6 +120,13 @@ impl MigratingManifest {
} }
} }
pub fn is_encrypted(&self) -> bool {
match self {
Self::SDA(manifest) => manifest.entries.iter().any(|e| e.encryption.is_some()),
Self::ManifestV1(manifest) => manifest.entries.iter().any(|e| e.encryption.is_some()),
}
}
pub fn load_all_accounts( pub fn load_all_accounts(
&self, &self,
folder: &Path, folder: &Path,
@ -164,15 +193,20 @@ impl From<MigratingManifest> for Manifest {
} }
} }
fn deserialize_manifest(text: String) -> anyhow::Result<MigratingManifest> { fn deserialize_manifest(text: String) -> Result<MigratingManifest, serde_json::Error> {
let json: serde_json::Value = serde_json::from_str(&text)?; let json: serde_json::Value = serde_json::from_str(&text)?;
debug!("deserializing manifest: version {}", json["version"]); debug!("deserializing manifest: version {}", json["version"]);
if json["version"] == 1 { if json["version"] == 1 {
let manifest: ManifestV1 = serde_json::from_str(&text)?; let manifest: ManifestV1 = serde_json::from_str(&text)?;
Ok(MigratingManifest::ManifestV1(manifest)) Ok(MigratingManifest::ManifestV1(manifest))
} else { } else if json["version"] == serde_json::Value::Null {
let manifest: SdaManifest = serde_json::from_str(&text)?; let manifest: SdaManifest = serde_json::from_str(&text)?;
Ok(MigratingManifest::SDA(manifest)) Ok(MigratingManifest::SDA(manifest))
} else {
Err(serde_json::Error::custom(format!(
"Unknown manifest version: {}",
json["version"]
)))
} }
} }

View file

@ -104,6 +104,7 @@ fn run() -> anyhow::Result<()> {
let (manifest, accounts) = load_and_migrate(path.as_path(), args.passkey.as_ref())?; let (manifest, accounts) = load_and_migrate(path.as_path(), args.passkey.as_ref())?;
let mut manager = AccountManager::from_manifest(manifest, mafiles_dir); let mut manager = AccountManager::from_manifest(manifest, mafiles_dir);
manager.register_accounts(accounts); manager.register_accounts(accounts);
manager.submit_passkey(args.passkey.clone());
manager.save()?; manager.save()?;
manager manager
} }
@ -114,7 +115,7 @@ fn run() -> anyhow::Result<()> {
} }
} }
let mut passkey: Option<String> = args.passkey.clone(); let mut passkey = args.passkey.clone();
manager.submit_passkey(passkey); manager.submit_passkey(passkey);
loop { loop {