2021-08-15 17:52:54 +02:00
use aes ::Aes256 ;
2021-08-16 05:20:49 +02:00
use block_modes ::block_padding ::{ NoPadding , Padding , Pkcs7 } ;
2021-08-15 17:52:54 +02:00
use block_modes ::{ BlockMode , Cbc } ;
2021-08-01 14:43:18 +02:00
use log ::* ;
2021-08-15 17:52:54 +02:00
use ring ::pbkdf2 ;
2021-08-16 05:20:49 +02:00
use ring ::rand ::SecureRandom ;
2021-08-01 14:43:18 +02:00
use serde ::{ Deserialize , Serialize } ;
2021-08-15 17:52:54 +02:00
use std ::fs ::File ;
use std ::io ::{ BufReader , Read , Write } ;
2021-03-26 00:47:44 +01:00
use std ::path ::Path ;
2021-08-01 18:34:13 +02:00
use std ::sync ::{ Arc , Mutex } ;
2021-07-31 18:24:49 +02:00
use steamguard ::SteamGuardAccount ;
2021-08-17 03:13:58 +02:00
use thiserror ::Error ;
2021-03-25 22:45:41 +01:00
2021-08-16 02:04:29 +02:00
type Aes256Cbc = Cbc < Aes256 , NoPadding > ;
2021-08-15 17:52:54 +02:00
2021-03-26 18:14:59 +01:00
#[ derive(Debug, Serialize, Deserialize) ]
2021-03-25 22:45:41 +01:00
pub struct Manifest {
2021-08-08 18:54:46 +02:00
pub encrypted : bool ,
pub entries : Vec < ManifestEntry > ,
2021-08-13 00:06:18 +02:00
/// Not implemented, kept for compatibility with SDA.
2021-08-08 18:54:46 +02:00
pub first_run : bool ,
2021-08-13 00:06:18 +02:00
/// Not implemented, kept for compatibility with SDA.
2021-08-08 18:54:46 +02:00
pub periodic_checking : bool ,
2021-08-13 00:06:18 +02:00
/// Not implemented, kept for compatibility with SDA.
2021-08-08 18:54:46 +02:00
pub periodic_checking_interval : i32 ,
2021-08-13 00:06:18 +02:00
/// Not implemented, kept for compatibility with SDA.
2021-08-08 18:54:46 +02:00
pub periodic_checking_checkall : bool ,
2021-08-13 00:06:18 +02:00
/// Not implemented, kept for compatibility with SDA.
2021-08-08 18:54:46 +02:00
pub auto_confirm_market_transactions : bool ,
2021-08-13 00:06:18 +02:00
/// Not implemented, kept for compatibility with SDA.
2021-08-08 18:54:46 +02:00
pub auto_confirm_trades : bool ,
2021-03-25 22:45:41 +01:00
2021-08-08 18:54:46 +02:00
#[ serde(skip) ]
pub accounts : Vec < Arc < Mutex < SteamGuardAccount > > > ,
#[ serde(skip) ]
folder : String , // I wanted to use a Path here, but it was too hard to make it work...
2021-03-25 22:45:41 +01:00
}
#[ derive(Debug, Clone, Serialize, Deserialize) ]
pub struct ManifestEntry {
2021-08-08 18:54:46 +02:00
pub filename : String ,
2021-08-15 02:47:29 +02:00
#[ serde(default, rename = " steamid " ) ]
2021-08-08 18:54:46 +02:00
pub steam_id : u64 ,
2021-08-15 02:47:29 +02:00
#[ serde(default) ]
2021-08-14 19:39:01 +02:00
pub account_name : String ,
2021-08-15 02:47:29 +02:00
#[ serde(default, flatten) ]
pub encryption : Option < EntryEncryptionParams > ,
}
#[ derive(Debug, Clone, Serialize, Deserialize) ]
pub struct EntryEncryptionParams {
#[ serde(rename = " encryption_iv " ) ]
pub iv : String ,
#[ serde(rename = " encryption_salt " ) ]
pub salt : String ,
2021-03-25 22:45:41 +01:00
}
2021-08-13 00:06:18 +02:00
impl Default for Manifest {
fn default ( ) -> Self {
Manifest {
encrypted : false ,
entries : vec ! [ ] ,
first_run : false ,
periodic_checking : false ,
periodic_checking_interval : 0 ,
periodic_checking_checkall : false ,
auto_confirm_market_transactions : false ,
auto_confirm_trades : false ,
accounts : vec ! [ ] ,
folder : " " . into ( ) ,
}
}
}
2021-03-26 00:47:44 +01:00
impl Manifest {
2021-08-13 00:06:18 +02:00
/// `path` should be the path to manifest.json
pub fn new ( path : & Path ) -> Self {
Manifest {
folder : String ::from ( path . parent ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ) ,
.. Default ::default ( )
}
}
pub fn load ( path : & Path ) -> anyhow ::Result < Self > {
2021-08-08 18:54:46 +02:00
debug! ( " loading manifest: {:?} " , & path ) ;
let file = File ::open ( path ) ? ;
let reader = BufReader ::new ( file ) ;
let mut manifest : Manifest = serde_json ::from_reader ( reader ) ? ;
manifest . folder = String ::from ( path . parent ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ) ;
return Ok ( manifest ) ;
}
2021-03-26 19:05:54 +01:00
2021-08-17 03:13:58 +02:00
pub fn load_accounts (
& mut self ,
passkey : & Option < String > ,
) -> anyhow ::Result < ( ) , ManifestAccountLoadError > {
2021-08-15 02:47:29 +02:00
for entry in & mut self . entries {
2021-08-08 18:54:46 +02:00
let path = Path ::new ( & self . folder ) . join ( & entry . filename ) ;
debug! ( " loading account: {:?} " , path ) ;
let file = File ::open ( path ) ? ;
2021-08-15 17:52:54 +02:00
let mut reader = BufReader ::new ( file ) ;
let account : SteamGuardAccount ;
match ( passkey , entry . encryption . as_ref ( ) ) {
( Some ( passkey ) , Some ( params ) ) = > {
let key = get_encryption_key ( & passkey . into ( ) , & params . salt ) ? ;
let iv = base64 ::decode ( & params . iv ) ? ;
let cipher = Aes256Cbc ::new_from_slices ( & key , & iv ) ? ;
2021-08-16 02:04:29 +02:00
2021-08-16 05:20:49 +02:00
// This sucks.
2021-08-15 17:52:54 +02:00
let mut ciphertext : Vec < u8 > = vec! [ ] ;
reader . read_to_end ( & mut ciphertext ) ? ;
2021-08-16 02:04:29 +02:00
ciphertext = base64 ::decode ( ciphertext ) ? ;
2021-08-19 18:54:52 +02:00
let size : usize =
ciphertext . len ( ) / 16 + ( if ciphertext . len ( ) % 16 = = 0 { 0 } else { 1 } ) ;
2021-08-16 02:04:29 +02:00
let mut buffer = vec! [ 0xff u8 ; 16 * size ] ;
buffer [ .. ciphertext . len ( ) ] . copy_from_slice ( & ciphertext ) ;
2021-08-19 15:55:52 +02:00
let mut decrypted = cipher . decrypt ( & mut buffer ) ? ;
if decrypted [ 0 ] ! = '{' as u8 {
return Err ( ManifestAccountLoadError ::DecryptionFailed ) ;
}
// UnpadError does not implement Error for some fucking reason, so we have to do this.
let unpadded = Pkcs7 ::unpad ( & mut decrypted )
2021-08-17 03:13:58 +02:00
. map_err ( | _ | ManifestAccountLoadError ::DecryptionFailed ) ? ;
2021-08-16 02:04:29 +02:00
2021-08-16 05:20:49 +02:00
let s = std ::str ::from_utf8 ( & unpadded ) . unwrap ( ) ;
account = serde_json ::from_str ( & s ) ? ;
2021-08-15 17:52:54 +02:00
}
( None , Some ( _ ) ) = > {
2021-08-17 03:13:58 +02:00
return Err ( ManifestAccountLoadError ::MissingPasskey ) ;
2021-08-15 17:52:54 +02:00
}
( _ , None ) = > {
account = serde_json ::from_reader ( reader ) ? ;
}
} ;
2021-08-15 02:47:29 +02:00
entry . account_name = account . account_name . clone ( ) ;
2021-08-08 18:54:46 +02:00
self . accounts . push ( Arc ::new ( Mutex ::new ( account ) ) ) ;
}
Ok ( ( ) )
}
2021-08-01 17:20:57 +02:00
2021-08-08 18:54:46 +02:00
pub fn add_account ( & mut self , account : SteamGuardAccount ) {
debug! ( " adding account to manifest: {} " , account . account_name ) ;
2021-08-14 19:39:01 +02:00
let steamid = account . session . as_ref ( ) . map_or ( 0 , | s | s . steam_id ) ;
2021-08-08 18:54:46 +02:00
self . entries . push ( ManifestEntry {
filename : format ! ( " {}.maFile " , & account . account_name ) ,
steam_id : steamid ,
2021-08-14 19:39:01 +02:00
account_name : account . account_name . clone ( ) ,
2021-08-15 02:47:29 +02:00
encryption : None ,
2021-08-08 18:54:46 +02:00
} ) ;
self . accounts . push ( Arc ::new ( Mutex ::new ( account ) ) ) ;
}
2021-08-01 17:20:57 +02:00
2021-08-14 01:04:03 +02:00
pub fn import_account ( & mut self , import_path : String ) -> anyhow ::Result < ( ) > {
let path = Path ::new ( & import_path ) ;
ensure! ( path . exists ( ) , " {} does not exist. " , import_path ) ;
ensure! ( path . is_file ( ) , " {} is not a file. " , import_path ) ;
let file = File ::open ( path ) ? ;
let reader = BufReader ::new ( file ) ;
let account : SteamGuardAccount = serde_json ::from_reader ( reader ) ? ;
self . add_account ( account ) ;
return Ok ( ( ) ) ;
}
2021-08-12 01:39:29 +02:00
pub fn remove_account ( & mut self , account_name : String ) {
let index = self
. accounts
. iter ( )
. position ( | a | a . lock ( ) . unwrap ( ) . account_name = = account_name )
. unwrap ( ) ;
self . accounts . remove ( index ) ;
self . entries . remove ( index ) ;
}
2021-08-17 03:13:58 +02:00
pub fn save ( & self , passkey : & Option < String > ) -> anyhow ::Result < ( ) > {
2021-08-08 18:54:46 +02:00
ensure! (
self . entries . len ( ) = = self . accounts . len ( ) ,
" Manifest entries don't match accounts. "
) ;
for ( entry , account ) in self . entries . iter ( ) . zip ( & self . accounts ) {
debug! ( " saving {} " , entry . filename ) ;
2021-08-16 05:20:49 +02:00
let serialized = serde_json ::to_vec ( account . as_ref ( ) ) ? ;
2021-08-08 18:54:46 +02:00
ensure! (
serialized . len ( ) > 2 ,
" Something extra weird happened and the account was serialized into nothing. "
) ;
2021-08-16 05:20:49 +02:00
let final_buffer : Vec < u8 > ;
match ( passkey , entry . encryption . as_ref ( ) ) {
( Some ( passkey ) , Some ( params ) ) = > {
let key = get_encryption_key ( & passkey . into ( ) , & params . salt ) ? ;
let iv = base64 ::decode ( & params . iv ) ? ;
let cipher = Aes256Cbc ::new_from_slices ( & key , & iv ) ? ;
2021-08-19 18:54:52 +02:00
// This also sucks. Extremely confusing.
2021-08-16 05:20:49 +02:00
let plaintext = serialized ;
2021-08-19 15:55:52 +02:00
let origsize = plaintext . len ( ) ;
2021-08-19 18:54:52 +02:00
let buffersize : usize =
( origsize / 16 + ( if origsize % 16 = = 0 { 0 } else { 1 } ) ) * 16 ;
let mut buffer = vec! [ ] ;
for chunk in plaintext . as_slice ( ) . chunks ( 256 ) {
let chunksize = chunk . len ( ) ;
let buffersize =
( chunksize / 16 + ( if chunksize % 16 = = 0 { 0 } else { 1 } ) ) * 16 ;
let mut chunkbuffer = vec! [ 0xff u8 ; buffersize ] ;
chunkbuffer [ .. chunksize ] . copy_from_slice ( & chunk ) ;
if buffersize ! = chunksize {
chunkbuffer = Pkcs7 ::pad ( & mut chunkbuffer , chunksize , buffersize )
. unwrap ( )
. to_vec ( ) ;
}
buffer . append ( & mut chunkbuffer ) ;
}
let ciphertext = cipher . encrypt ( & mut buffer , buffersize ) ? ;
2021-08-16 05:20:49 +02:00
final_buffer = base64 ::encode ( & ciphertext ) . as_bytes ( ) . to_vec ( ) ;
}
( None , Some ( _ ) ) = > {
bail! ( " maFiles are encrypted, but no passkey was provided. " ) ;
}
( _ , None ) = > {
final_buffer = serialized ;
}
} ;
2021-08-08 18:54:46 +02:00
let path = Path ::new ( & self . folder ) . join ( & entry . filename ) ;
let mut file = File ::create ( path ) ? ;
2021-08-16 05:20:49 +02:00
file . write_all ( final_buffer . as_slice ( ) ) ? ;
2021-08-08 18:54:46 +02:00
file . sync_data ( ) ? ;
}
debug! ( " saving manifest " ) ;
let manifest_serialized = serde_json ::to_string ( & self ) ? ;
let path = Path ::new ( & self . folder ) . join ( " manifest.json " ) ;
let mut file = File ::create ( path ) ? ;
file . write_all ( manifest_serialized . as_bytes ( ) ) ? ;
file . sync_data ( ) ? ;
Ok ( ( ) )
}
2021-03-26 00:47:44 +01:00
}
2021-08-14 19:39:01 +02:00
2021-08-16 05:20:49 +02:00
const PBKDF2_ITERATIONS : u32 = 50000 ; // This is excessive. Is this needed to remain compatible with SDA?
2021-08-15 17:52:54 +02:00
const SALT_LENGTH : usize = 8 ;
const KEY_SIZE_BYTES : usize = 32 ;
const IV_LENGTH : usize = 16 ;
fn get_encryption_key ( passkey : & String , salt : & String ) -> anyhow ::Result < [ u8 ; KEY_SIZE_BYTES ] > {
let password_bytes = passkey . as_bytes ( ) ;
let salt_bytes = base64 ::decode ( salt ) ? ;
let mut full_key : [ u8 ; KEY_SIZE_BYTES ] = [ 0 u8 ; KEY_SIZE_BYTES ] ;
pbkdf2 ::derive (
2021-08-15 19:41:11 +02:00
pbkdf2 ::PBKDF2_HMAC_SHA1 ,
2021-08-15 17:52:54 +02:00
std ::num ::NonZeroU32 ::new ( PBKDF2_ITERATIONS ) . unwrap ( ) ,
& salt_bytes ,
password_bytes ,
& mut full_key ,
) ;
return Ok ( full_key ) ;
}
2021-08-16 05:20:49 +02:00
impl EntryEncryptionParams {
pub fn generate ( ) -> EntryEncryptionParams {
let rng = ring ::rand ::SystemRandom ::new ( ) ;
let mut salt = [ 0 u8 ; SALT_LENGTH ] ;
let mut iv = [ 0 u8 ; IV_LENGTH ] ;
rng . fill ( & mut salt ) . expect ( " Unable to generate salt. " ) ;
rng . fill ( & mut iv ) . expect ( " Unable to generate IV. " ) ;
EntryEncryptionParams {
salt : base64 ::encode ( salt ) ,
iv : base64 ::encode ( iv ) ,
}
}
}
2021-08-17 03:13:58 +02:00
#[ derive(Debug, Error) ]
pub enum ManifestAccountLoadError {
#[ error( " Manifest accounts are encrypted, but no passkey was provided. " ) ]
MissingPasskey ,
#[ error( " Failed to decrypt account. " ) ]
#[ from(block_modes::block_padding::UnpadError) ]
DecryptionFailed ,
#[ error( " Failed to deserialize the account. " ) ]
DeserializationFailed ( #[ from ] serde_json ::Error ) ,
#[ error(transparent) ]
Unknown ( #[ from ] anyhow ::Error ) ,
}
/// For some reason, these errors do not get converted to `ManifestAccountLoadError`s, even though they get converted into `anyhow::Error` just fine. I am too lazy to figure out why right now.
impl From < block_modes ::BlockModeError > for ManifestAccountLoadError {
fn from ( error : block_modes ::BlockModeError ) -> Self {
return Self ::Unknown ( anyhow ::Error ::from ( error ) ) ;
}
}
impl From < base64 ::DecodeError > for ManifestAccountLoadError {
fn from ( error : base64 ::DecodeError ) -> Self {
return Self ::Unknown ( anyhow ::Error ::from ( error ) ) ;
}
}
impl From < block_modes ::InvalidKeyIvLength > for ManifestAccountLoadError {
fn from ( error : block_modes ::InvalidKeyIvLength ) -> Self {
return Self ::Unknown ( anyhow ::Error ::from ( error ) ) ;
}
}
impl From < std ::io ::Error > for ManifestAccountLoadError {
fn from ( error : std ::io ::Error ) -> Self {
return Self ::Unknown ( anyhow ::Error ::from ( error ) ) ;
}
}
2021-08-14 19:39:01 +02:00
#[ cfg(test) ]
mod tests {
use super ::* ;
use tempdir ::TempDir ;
#[ test ]
fn test_should_save_new_manifest ( ) {
let tmp_dir = TempDir ::new ( " steamguard-cli-test " ) . unwrap ( ) ;
let manifest_path = tmp_dir . path ( ) . join ( " manifest.json " ) ;
let manifest = Manifest ::new ( manifest_path . as_path ( ) ) ;
2021-08-17 03:13:58 +02:00
assert! ( matches! ( manifest . save ( & None ) , Ok ( _ ) ) ) ;
2021-08-14 19:39:01 +02:00
}
#[ test ]
fn test_should_save_and_load_manifest ( ) {
let tmp_dir = TempDir ::new ( " steamguard-cli-test " ) . unwrap ( ) ;
let manifest_path = tmp_dir . path ( ) . join ( " manifest.json " ) ;
let mut manifest = Manifest ::new ( manifest_path . as_path ( ) ) ;
let mut account = SteamGuardAccount ::new ( ) ;
account . account_name = " asdf1234 " . into ( ) ;
account . revocation_code = " R12345 " . into ( ) ;
account . shared_secret = " secret " . into ( ) ;
manifest . add_account ( account ) ;
2021-08-17 03:13:58 +02:00
assert! ( matches! ( manifest . save ( & None ) , Ok ( _ ) ) ) ;
2021-08-14 19:39:01 +02:00
let mut loaded_manifest = Manifest ::load ( manifest_path . as_path ( ) ) . unwrap ( ) ;
assert_eq! ( loaded_manifest . entries . len ( ) , 1 ) ;
assert_eq! ( loaded_manifest . entries [ 0 ] . filename , " asdf1234.maFile " ) ;
2021-08-17 03:13:58 +02:00
assert! ( matches! ( loaded_manifest . load_accounts ( & None ) , Ok ( _ ) ) ) ;
2021-08-14 19:39:01 +02:00
assert_eq! (
loaded_manifest . entries . len ( ) ,
loaded_manifest . accounts . len ( )
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . account_name ,
" asdf1234 "
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . revocation_code ,
" R12345 "
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . shared_secret ,
" secret "
) ;
}
2021-08-16 05:20:49 +02:00
#[ test ]
fn test_should_save_and_load_manifest_encrypted ( ) {
2021-08-17 03:13:58 +02:00
let passkey : Option < String > = Some ( " password " . into ( ) ) ;
2021-08-16 05:20:49 +02:00
let tmp_dir = TempDir ::new ( " steamguard-cli-test " ) . unwrap ( ) ;
let manifest_path = tmp_dir . path ( ) . join ( " manifest.json " ) ;
let mut manifest = Manifest ::new ( manifest_path . as_path ( ) ) ;
let mut account = SteamGuardAccount ::new ( ) ;
account . account_name = " asdf1234 " . into ( ) ;
account . revocation_code = " R12345 " . into ( ) ;
account . shared_secret = " secret " . into ( ) ;
manifest . add_account ( account ) ;
manifest . entries [ 0 ] . encryption = Some ( EntryEncryptionParams ::generate ( ) ) ;
2021-08-17 03:13:58 +02:00
assert! ( matches! ( manifest . save ( & passkey ) , Ok ( _ ) ) ) ;
2021-08-16 05:20:49 +02:00
let mut loaded_manifest = Manifest ::load ( manifest_path . as_path ( ) ) . unwrap ( ) ;
assert_eq! ( loaded_manifest . entries . len ( ) , 1 ) ;
assert_eq! ( loaded_manifest . entries [ 0 ] . filename , " asdf1234.maFile " ) ;
2021-08-17 03:13:58 +02:00
assert! ( matches! ( loaded_manifest . load_accounts ( & passkey ) , Ok ( _ ) ) ) ;
2021-08-16 05:20:49 +02:00
assert_eq! (
loaded_manifest . entries . len ( ) ,
loaded_manifest . accounts . len ( )
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . account_name ,
" asdf1234 "
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . revocation_code ,
" R12345 "
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . shared_secret ,
" secret "
) ;
}
2021-08-19 15:55:52 +02:00
#[ test ]
fn test_should_save_and_load_manifest_encrypted_longer ( ) -> anyhow ::Result < ( ) > {
let passkey : Option < String > = Some ( " password " . into ( ) ) ;
let tmp_dir = TempDir ::new ( " steamguard-cli-test " ) . unwrap ( ) ;
let manifest_path = tmp_dir . path ( ) . join ( " manifest.json " ) ;
let mut manifest = Manifest ::new ( manifest_path . as_path ( ) ) ;
let mut account = SteamGuardAccount ::new ( ) ;
account . account_name = " asdf1234 " . into ( ) ;
account . revocation_code = " R12345 " . into ( ) ;
account . shared_secret = " secret " . into ( ) ;
account . uri = " otpauth://;laksdjf;lkasdjf;lkasdj;flkasdjlkf;asjdlkfjslk;adjfl;kasdjf;lksdjflk;asjd;lfajs;ldkfjaslk;djf;lsakdjf;lksdj " . into ( ) ;
account . token_gid = " asdf1234 " . into ( ) ;
manifest . add_account ( account ) ;
manifest . entries [ 0 ] . encryption = Some ( EntryEncryptionParams ::generate ( ) ) ;
manifest . save ( & passkey ) ? ;
let mut loaded_manifest = Manifest ::load ( manifest_path . as_path ( ) ) ? ;
assert_eq! ( loaded_manifest . entries . len ( ) , 1 ) ;
assert_eq! ( loaded_manifest . entries [ 0 ] . filename , " asdf1234.maFile " ) ;
loaded_manifest . load_accounts ( & passkey ) ? ;
assert_eq! (
loaded_manifest . entries . len ( ) ,
loaded_manifest . accounts . len ( )
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . account_name ,
" asdf1234 "
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . revocation_code ,
" R12345 "
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . shared_secret ,
" secret "
) ;
return Ok ( ( ) ) ;
}
2021-08-14 19:39:01 +02:00
#[ test ]
fn test_should_import ( ) {
let tmp_dir = TempDir ::new ( " steamguard-cli-test " ) . unwrap ( ) ;
let manifest_path = tmp_dir . path ( ) . join ( " manifest.json " ) ;
let mut manifest = Manifest ::new ( manifest_path . as_path ( ) ) ;
let mut account = SteamGuardAccount ::new ( ) ;
account . account_name = " asdf1234 " . into ( ) ;
account . revocation_code = " R12345 " . into ( ) ;
account . shared_secret = " secret " . into ( ) ;
manifest . add_account ( account ) ;
2021-08-17 03:13:58 +02:00
assert! ( matches! ( manifest . save ( & None ) , Ok ( _ ) ) ) ;
2021-08-14 19:39:01 +02:00
std ::fs ::remove_file ( & manifest_path ) . unwrap ( ) ;
let mut loaded_manifest = Manifest ::new ( manifest_path . as_path ( ) ) ;
assert! ( matches! (
loaded_manifest . import_account (
tmp_dir
. path ( )
. join ( " asdf1234.maFile " )
. into_os_string ( )
. into_string ( )
. unwrap ( )
) ,
Ok ( _ )
) ) ;
assert_eq! (
loaded_manifest . entries . len ( ) ,
loaded_manifest . accounts . len ( )
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . account_name ,
" asdf1234 "
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . revocation_code ,
" R12345 "
) ;
assert_eq! (
loaded_manifest . accounts [ 0 ] . lock ( ) . unwrap ( ) . shared_secret ,
" secret "
) ;
}
2021-08-15 02:47:29 +02:00
#[ test ]
fn test_sda_compatibility_1 ( ) {
2021-08-15 04:51:03 +02:00
let path = Path ::new ( " src/fixtures/maFiles/1-account/manifest.json " ) ;
2021-08-15 02:47:29 +02:00
assert! ( path . is_file ( ) ) ;
let result = Manifest ::load ( path ) ;
assert! ( matches! ( result , Ok ( _ ) ) ) ;
let mut manifest = result . unwrap ( ) ;
assert! ( matches! ( manifest . entries . last ( ) . unwrap ( ) . encryption , None ) ) ;
2021-08-17 03:13:58 +02:00
assert! ( matches! ( manifest . load_accounts ( & None ) , Ok ( _ ) ) ) ;
2021-08-15 02:47:29 +02:00
assert_eq! (
manifest . entries . last ( ) . unwrap ( ) . account_name ,
manifest
. accounts
. last ( )
. unwrap ( )
. lock ( )
. unwrap ( )
. account_name
) ;
}
2021-08-15 17:52:54 +02:00
#[ test ]
fn test_sda_compatibility_1_encrypted ( ) {
let path = Path ::new ( " src/fixtures/maFiles/1-account-encrypted/manifest.json " ) ;
assert! ( path . is_file ( ) ) ;
let result = Manifest ::load ( path ) ;
assert! ( matches! ( result , Ok ( _ ) ) ) ;
let mut manifest = result . unwrap ( ) ;
assert! ( matches! (
manifest . entries . last ( ) . unwrap ( ) . encryption ,
Some ( _ )
) ) ;
2021-08-17 03:13:58 +02:00
let result = manifest . load_accounts ( & Some ( " password " . into ( ) ) ) ;
2021-08-15 17:52:54 +02:00
assert! (
matches! ( result , Ok ( _ ) ) ,
" error when loading accounts: {:?} " ,
result . unwrap_err ( )
) ;
assert_eq! (
manifest . entries . last ( ) . unwrap ( ) . account_name ,
manifest
. accounts
. last ( )
. unwrap ( )
. lock ( )
. unwrap ( )
. account_name
) ;
}
2021-08-14 19:39:01 +02:00
}
2021-08-15 19:41:11 +02:00
#[ cfg(test) ]
mod encryption_tests {
use super ::* ;
/// This test ensures compatibility with SteamDesktopAuthenticator and with previous versions of steamguard-cli
#[ test ]
fn test_encryption_key ( ) {
assert_eq! (
get_encryption_key ( & " password " . into ( ) , & " GMhL0N2hqXg= " . into ( ) ) . unwrap ( ) ,
base64 ::decode ( " KtiRa4/OxW83MlB6URf+Z8rAGj7CBY+pDlwD/NuVo6Y= " )
. unwrap ( )
. as_slice ( )
) ;
assert_eq! (
get_encryption_key ( & " password " . into ( ) , & " wTzTE9A6aN8= " . into ( ) ) . unwrap ( ) ,
base64 ::decode ( " Dqpej/3DqEat0roJaHmu3luYgDzRCUmzX94n4fqvWj8= " )
. unwrap ( )
. as_slice ( )
) ;
}
}