2023-06-23 13:36:23 -04:00
|
|
|
#![allow(deprecated)]
|
|
|
|
|
2023-06-22 16:20:15 -04:00
|
|
|
use std::{
|
|
|
|
fs::File,
|
|
|
|
io::{BufReader, Read},
|
|
|
|
path::Path,
|
|
|
|
};
|
|
|
|
|
|
|
|
use log::debug;
|
2023-06-23 13:36:23 -04:00
|
|
|
use secrecy::{CloneableSecret, DebugSecret, ExposeSecret};
|
2023-06-22 16:20:15 -04:00
|
|
|
use serde::Deserialize;
|
|
|
|
use steamguard::{token::TwoFactorSecret, SecretString, SteamGuardAccount};
|
2023-06-23 13:36:23 -04:00
|
|
|
use zeroize::Zeroize;
|
2023-06-22 16:20:15 -04:00
|
|
|
|
|
|
|
use crate::encryption::{EncryptionScheme, EntryEncryptor};
|
|
|
|
|
|
|
|
use super::{
|
|
|
|
EntryEncryptionParams, EntryLoader, ManifestAccountLoadError, ManifestEntry, ManifestV1,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct SdaManifest {
|
|
|
|
#[serde(default)]
|
|
|
|
pub version: u32,
|
|
|
|
pub entries: Vec<SdaManifestEntry>,
|
|
|
|
/// Not really used, kept mostly for compatibility with SDA.
|
|
|
|
pub encrypted: bool,
|
|
|
|
/// Not implemented, kept for compatibility with SDA.
|
|
|
|
pub first_run: bool,
|
|
|
|
/// Not implemented, kept for compatibility with SDA.
|
|
|
|
pub periodic_checking: bool,
|
|
|
|
/// Not implemented, kept for compatibility with SDA.
|
|
|
|
pub periodic_checking_interval: i32,
|
|
|
|
/// Not implemented, kept for compatibility with SDA.
|
|
|
|
pub periodic_checking_checkall: bool,
|
|
|
|
/// Not implemented, kept for compatibility with SDA.
|
|
|
|
pub auto_confirm_market_transactions: bool,
|
|
|
|
/// Not implemented, kept for compatibility with SDA.
|
|
|
|
pub auto_confirm_trades: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<SdaManifest> for ManifestV1 {
|
|
|
|
fn from(sda: SdaManifest) -> Self {
|
|
|
|
Self {
|
|
|
|
version: 1,
|
|
|
|
entries: sda.entries.into_iter().map(|e| e.into()).collect(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
|
|
pub struct SdaManifestEntry {
|
|
|
|
pub filename: String,
|
|
|
|
#[serde(default, rename = "steamid")]
|
|
|
|
pub steam_id: u64,
|
|
|
|
#[serde(default, flatten)]
|
|
|
|
pub encryption: Option<SdaEntryEncryptionParams>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<SdaManifestEntry> for ManifestEntry {
|
|
|
|
fn from(sda: SdaManifestEntry) -> Self {
|
|
|
|
Self {
|
|
|
|
filename: sda.filename,
|
|
|
|
steam_id: sda.steam_id,
|
|
|
|
account_name: Default::default(),
|
|
|
|
encryption: sda.encryption.map(|e| e.into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EntryLoader<SdaAccount> for SdaManifestEntry {
|
|
|
|
fn load(
|
|
|
|
&self,
|
|
|
|
path: &Path,
|
|
|
|
passkey: Option<&String>,
|
|
|
|
encryption_params: Option<&EntryEncryptionParams>,
|
|
|
|
) -> anyhow::Result<SdaAccount, ManifestAccountLoadError> {
|
|
|
|
debug!("loading entry: {:?}", path);
|
|
|
|
let file = File::open(path)?;
|
|
|
|
let mut reader = BufReader::new(file);
|
2023-06-23 13:36:23 -04:00
|
|
|
let account: SdaAccount = match (&passkey, encryption_params.as_ref()) {
|
2023-06-22 16:20:15 -04:00
|
|
|
(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)?;
|
|
|
|
if plaintext[0] != b'{' && plaintext[plaintext.len() - 1] != b'}' {
|
|
|
|
return Err(ManifestAccountLoadError::IncorrectPasskey);
|
|
|
|
}
|
|
|
|
let s = std::str::from_utf8(&plaintext).unwrap();
|
2023-06-24 12:06:12 -04:00
|
|
|
let mut deser = serde_json::Deserializer::from_str(s);
|
|
|
|
serde_path_to_error::deserialize(&mut deser)?
|
2023-06-22 16:20:15 -04:00
|
|
|
}
|
|
|
|
(None, Some(_)) => {
|
|
|
|
return Err(ManifestAccountLoadError::MissingPasskey);
|
|
|
|
}
|
2023-06-24 12:06:12 -04:00
|
|
|
(_, None) => {
|
|
|
|
let mut deser = serde_json::Deserializer::from_reader(reader);
|
|
|
|
serde_path_to_error::deserialize(&mut deser)?
|
|
|
|
}
|
2023-06-22 16:20:15 -04:00
|
|
|
};
|
|
|
|
Ok(account)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
|
|
pub struct SdaEntryEncryptionParams {
|
|
|
|
#[serde(rename = "encryption_iv")]
|
|
|
|
pub iv: String,
|
|
|
|
#[serde(rename = "encryption_salt")]
|
|
|
|
pub salt: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<SdaEntryEncryptionParams> for EntryEncryptionParams {
|
|
|
|
fn from(sda: SdaEntryEncryptionParams) -> Self {
|
|
|
|
Self {
|
|
|
|
iv: sda.iv,
|
|
|
|
salt: sda.salt,
|
|
|
|
scheme: EncryptionScheme::LegacySdaCompatible,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
|
|
pub struct SdaAccount {
|
|
|
|
pub account_name: String,
|
|
|
|
pub serial_number: String,
|
|
|
|
#[serde(with = "crate::secret_string")]
|
|
|
|
pub revocation_code: SecretString,
|
|
|
|
pub shared_secret: TwoFactorSecret,
|
|
|
|
pub token_gid: String,
|
|
|
|
#[serde(with = "crate::secret_string")]
|
|
|
|
pub identity_secret: SecretString,
|
|
|
|
pub server_time: u64,
|
|
|
|
#[serde(with = "crate::secret_string")]
|
|
|
|
pub uri: SecretString,
|
|
|
|
pub fully_enrolled: bool,
|
|
|
|
pub device_id: String,
|
|
|
|
#[serde(with = "crate::secret_string")]
|
|
|
|
pub secret_1: SecretString,
|
|
|
|
#[serde(default, rename = "Session")]
|
2023-06-23 13:36:23 -04:00
|
|
|
pub session: Option<secrecy::Secret<Session>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Deserialize, Zeroize)]
|
|
|
|
#[zeroize(drop)]
|
|
|
|
#[deprecated(note = "this is not used anymore, the closest equivalent is `Tokens`")]
|
|
|
|
pub struct Session {
|
2023-06-24 08:17:56 -04:00
|
|
|
#[serde(default, rename = "SessionID")]
|
2023-06-23 13:36:23 -04:00
|
|
|
pub session_id: String,
|
2023-06-24 08:17:56 -04:00
|
|
|
#[serde(default, rename = "SteamLogin")]
|
2023-06-23 13:36:23 -04:00
|
|
|
pub steam_login: String,
|
2023-06-24 08:17:56 -04:00
|
|
|
#[serde(default, rename = "SteamLoginSecure")]
|
2023-06-23 13:36:23 -04:00
|
|
|
pub steam_login_secure: String,
|
|
|
|
#[serde(default, rename = "WebCookie")]
|
|
|
|
pub web_cookie: Option<String>,
|
2023-06-24 08:17:56 -04:00
|
|
|
#[serde(default, rename = "OAuthToken")]
|
2023-06-23 13:36:23 -04:00
|
|
|
pub token: String,
|
|
|
|
#[serde(rename = "SteamID")]
|
|
|
|
pub steam_id: u64,
|
2023-06-22 16:20:15 -04:00
|
|
|
}
|
|
|
|
|
2023-06-23 13:36:23 -04:00
|
|
|
impl CloneableSecret for Session {}
|
|
|
|
impl DebugSecret for Session {}
|
|
|
|
|
2023-06-22 16:20:15 -04:00
|
|
|
impl From<SdaAccount> for SteamGuardAccount {
|
|
|
|
fn from(value: SdaAccount) -> Self {
|
|
|
|
let steam_id = value
|
|
|
|
.session
|
|
|
|
.as_ref()
|
|
|
|
.map(|s| s.expose_secret().steam_id)
|
|
|
|
.unwrap_or(0);
|
|
|
|
Self {
|
|
|
|
account_name: value.account_name,
|
|
|
|
steam_id,
|
|
|
|
serial_number: value.serial_number,
|
|
|
|
revocation_code: value.revocation_code,
|
|
|
|
shared_secret: value.shared_secret,
|
|
|
|
token_gid: value.token_gid,
|
|
|
|
identity_secret: value.identity_secret,
|
|
|
|
uri: value.uri,
|
|
|
|
device_id: value.device_id,
|
|
|
|
secret_1: value.secret_1,
|
|
|
|
tokens: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|