From 5b8d581a48539eb9bd1965d68df513932ed13a82 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sun, 21 Mar 2021 21:21:29 -0400 Subject: [PATCH 01/36] add working 2fa code generation --- .gitignore | 4 ++- Cargo.lock | 30 +++++++++++++++++ Cargo.toml | 11 +++++++ src/lib.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 11 +++++++ 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore index 277cae8..426234d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ obj/ SteamAuth/SteamAuth/packages/ test_maFiles/ -*.deb \ No newline at end of file +*.deb + +target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f5b9a8f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,30 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "hmac-sha1" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1333fad8d94b82cab989da428b0b36a3435db3870d85e971a1d6dc0a8576722" +dependencies = [ + "sha1", +] + +[[package]] +name = "sha1" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" + +[[package]] +name = "steamguard-cli" +version = "0.2.0" +dependencies = [ + "base64", + "hmac-sha1", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b71ab37 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "steamguard-cli" +version = "0.2.0" +authors = ["Carson McManus "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hmac-sha1 = "^0.1" +base64 = "0.13.0" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f5b8c6d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,92 @@ +use std::convert::TryInto; + +// const STEAMAPI_BASE: String = "https://api.steampowered.com"; +// const COMMUNITY_BASE: String = "https://steamcommunity.com"; +// const MOBILEAUTH_BASE: String = STEAMAPI_BASE + "/IMobileAuthService/%s/v0001"; +// static MOBILEAUTH_GETWGTOKEN: String = MOBILEAUTH_BASE.Replace("%s", "GetWGToken"); +// const TWO_FACTOR_BASE: String = STEAMAPI_BASE + "/ITwoFactorService/%s/v0001"; +// static TWO_FACTOR_TIME_QUERY: String = TWO_FACTOR_BASE.Replace("%s", "QueryTime"); + +extern crate hmacsha1; +extern crate base64; + +#[derive(Debug)] +pub struct SteamGuardAccount { + pub account_name: String, + pub revocation_code: String, + pub shared_secret: [u8; 20], +} + +fn build_time_bytes(mut time: i64) -> [u8; 8] { + time /= 30i64; + + let mut bytes: [u8; 8] = [0; 8]; + for i in (0..8).rev() { + bytes[i] = time as u8; + time >>= 8; + } + return bytes +} + +pub fn parse_shared_secret(secret: String) -> [u8; 20] { + if secret.len() == 0 { + panic!("unable to parse empty shared secret") + } + match base64::decode(secret) { + Result::Ok(v) => { + return v.try_into().unwrap() + } + _ => { + panic!("unable to parse shared secret") + } + } +} + +impl SteamGuardAccount { + pub fn new() -> Self { + return SteamGuardAccount{ + account_name: String::from(""), + revocation_code: String::from(""), + shared_secret: [0; 20], + } + } + + pub fn generate_code(&self, time: i64) -> String { + let steam_guard_code_translations: [u8; 26] = [50, 51, 52, 53, 54, 55, 56, 57, 66, 67, 68, 70, 71, 72, 74, 75, 77, 78, 80, 81, 82, 84, 86, 87, 88, 89]; + + let time_bytes: [u8; 8] = build_time_bytes(time); + // println!("time_bytes: {:?}", time_bytes); + let hashed_data = hmacsha1::hmac_sha1(&self.shared_secret, &time_bytes); + // println!("hashed_data: {:?}", hashed_data); + let mut code_array: [u8; 5] = [0; 5]; + let b = (hashed_data[19] & 0xF) as usize; + let mut code_point: i32 = + ((hashed_data[b] & 0x7F) as i32) << 24 | + ((hashed_data[b + 1] & 0xFF) as i32) << 16 | + ((hashed_data[b + 2] & 0xFF) as i32) << 8 | + ((hashed_data[b + 3] & 0xFF) as i32); + + for i in 0..5 { + code_array[i] = steam_guard_code_translations[code_point as usize % steam_guard_code_translations.len()]; + code_point /= steam_guard_code_translations.len() as i32; + } + + // println!("code_array: {:?}", code_array); + + return String::from_utf8(code_array.iter().map(|c| *c).collect()).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_generate_code() { + let mut account = SteamGuardAccount::new(); + account.shared_secret = parse_shared_secret(String::from("zvIayp3JPvtvX/QGHqsqKBk/44s=")); + + let code = account.generate_code(1616374841i64); + assert_eq!(code, "2F9J5") + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1bbbf2e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,11 @@ +use steamguard_cli::*; + +fn main() { + println!("Hello, world!"); + + let mut account = SteamGuardAccount::new(); + account.shared_secret = parse_shared_secret(String::from("K5I0Fmm+sN0yF41vIslTVm+0nPE=")); + + let code = account.generate_code(93847539487i64); + println!("{}", code) +} From 142f394d98ea453408df470d9767c702d8c33392 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Wed, 24 Mar 2021 17:49:09 -0400 Subject: [PATCH 02/36] add working RSA encryption for password during login --- Cargo.lock | 1598 ++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 7 + src/main.rs | 22 +- src/steamapi.rs | 136 ++++ 4 files changed, 1760 insertions(+), 3 deletions(-) create mode 100644 src/steamapi.rs diff --git a/Cargo.lock b/Cargo.lock index f5b9a8f..18e16eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,18 +1,1081 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "async-compression" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72c1f1154e234325b50864a349b9c8e56939e266a4c307c0f159812df2f9537" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + [[package]] name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time 0.1.43", + "winapi", +] + +[[package]] +name = "const_fn" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" + +[[package]] +name = "cookie" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" +dependencies = [ + "percent-encoding", + "time 0.2.26", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3" +dependencies = [ + "cookie", + "idna", + "log", + "publicsuffix", + "serde", + "serde_json", + "time 0.2.26", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + +[[package]] +name = "encoding_rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "flate2" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" + +[[package]] +name = "futures-io" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" + +[[package]] +name = "futures-sink" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" + +[[package]] +name = "futures-task" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" + +[[package]] +name = "futures-util" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", +] + +[[package]] +name = "h2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d832b01df74254fe364568d6ddc294443f61cbec82816b60904303af87efae78" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + [[package]] name = "hmac-sha1" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1333fad8d94b82cab989da428b0b36a3435db3870d85e971a1d6dc0a8576722" dependencies = [ - "sha1", + "sha1 0.2.0", +] + +[[package]] +name = "http" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" + +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + +[[package]] +name = "hyper" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +dependencies = [ + "autocfg 1.0.1", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "js-sys" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae" + +[[package]] +name = "libm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg 1.0.1", +] + +[[package]] +name = "mio" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2182a122f3b7f3f5329cb1972cee089ba2459a0a80a56935e6e674f096f8d839" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +dependencies = [ + "socket2", + "winapi", +] + +[[package]] +name = "native-tls" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d51546d704f52ef14b3c962b5776e53d5b862e5790e40a350d366c209bd7f7a" +dependencies = [ + "autocfg 0.1.7", + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.7.3", + "serde", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg 1.0.1", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg 1.0.1", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f" +dependencies = [ + "autocfg 1.0.1", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64", + "once_cell", + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "publicsuffix" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b4ce31ff0a27d93c8de1849cf58162283752f065a90d508f1105fa6c9a213f" +dependencies = [ + "idna", + "url", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.2", + "rand_hc 0.3.0", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.2", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom 0.2.2", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf12057f289428dbf5c591c74bf10392e4a8003f993405a902f20117019022d4" +dependencies = [ + "async-compression", + "base64", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "time 0.2.26", + "tokio", + "tokio-native-tls", + "tokio-util", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rpassword" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "rsa" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3648b669b10afeab18972c105e284a7b953a669b0be3514c27f9b17acab2f9cd" +dependencies = [ + "byteorder", + "digest", + "lazy_static", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pem", + "rand 0.7.3", + "sha2", + "simple_asn1", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "security-framework" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d493c5f39e02dfb062cd8f33301f90f9b13b650e8c1b1d0fd75c19dd64bff69d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee48cdde5ed250b0d3252818f646e174ab414036edb884dde62d80a3ac6082d" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", ] [[package]] @@ -21,10 +1084,543 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + +[[package]] +name = "sha2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" +dependencies = [ + "block-buffer", + "cfg-if", + "cpuid-bool", + "digest", + "opaque-debug", +] + +[[package]] +name = "simple_asn1" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" +dependencies = [ + "chrono", + "num-bigint", + "num-traits", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if", + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "standback" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "decebb311175fdaf1bf8a14583716e93163c566db2ead1c1d608c3e1e4313cb8" +dependencies = [ + "version_check", +] + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1 0.6.0", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + [[package]] name = "steamguard-cli" version = "0.2.0" dependencies = [ "base64", "hmac-sha1", + "rand 0.7.3", + "reqwest", + "rpassword", + "rsa", + "serde", + "serde_json", + "text_io", +] + +[[package]] +name = "subtle" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" + +[[package]] +name = "syn" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand 0.8.3", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "text_io" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb170b4f47dc48835fbc56259c12d8963e542b05a24be2e3a1f5a6c320fd2d4" + +[[package]] +name = "thiserror" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "time" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a8cbfbf47955132d0202d1662f49b2423ae35862aee471f3ba4b133358f372" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb", + "time-macros", + "version_check", + "winapi", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722" +dependencies = [ + "autocfg 1.0.1", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "url" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe" +dependencies = [ + "cfg-if", + "serde", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73157efb9af26fb564bb59a009afd1c7c334a44db171d280690d0c3faaec3468" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa" + +[[package]] +name = "web-sys" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + +[[package]] +name = "zeroize" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f369ddb18862aba61aa49bf31e74d29f0f162dec753063200e1dc084345d16" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index b71ab37..acd79a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,10 @@ edition = "2018" [dependencies] hmac-sha1 = "^0.1" base64 = "0.13.0" +text_io = "0.1.8" +rpassword = "5.0" +reqwest = { version = "0.11", features = ["blocking", "json", "cookies", "gzip"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +rsa = "0.3" +rand = "0.7.3" # rsa is not compatible with rand 0.8: https://github.com/RustCrypto/RSA/issues/81 diff --git a/src/main.rs b/src/main.rs index 1bbbf2e..a04b62a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,29 @@ +extern crate rpassword; +use io::Write; use steamguard_cli::*; +use ::std::*; +use text_io::read; + +mod steamapi; fn main() { println!("Hello, world!"); + let server_time = steamapi::get_server_time(); + println!("server time: {}", server_time); + let mut account = SteamGuardAccount::new(); account.shared_secret = parse_shared_secret(String::from("K5I0Fmm+sN0yF41vIslTVm+0nPE=")); - let code = account.generate_code(93847539487i64); - println!("{}", code) + let code = account.generate_code(server_time); + println!("{}", code); + + print!("Username: "); + let _ = std::io::stdout().flush(); + let username: String = read!("{}\n"); + let password = rpassword::prompt_password_stdout("Password: ").unwrap(); + // println!("{}:{}", username, password); + let login = steamapi::UserLogin::new(username, password); + let result = login.login(); + println!("result: {:?}", result); } diff --git a/src/steamapi.rs b/src/steamapi.rs new file mode 100644 index 0000000..fd9e41a --- /dev/null +++ b/src/steamapi.rs @@ -0,0 +1,136 @@ +use std::collections::HashMap; +use reqwest::{Url, cookie::CookieStore, header::COOKIE, header::USER_AGENT}; +use rsa::{PublicKey, RSAPublicKey}; +use std::time::{SystemTime, UNIX_EPOCH}; +use serde::{Serialize, Deserialize}; +use rand::rngs::OsRng; + +#[derive(Debug, Deserialize)] +struct LoginResponse { + success: bool, + login_complete: bool, + oauth_data_string: String, +} + +#[derive(Debug, Deserialize)] +struct RsaResponse { + success: bool, + publickey_exp: String, + publickey_mod: String, + timestamp: String, + token_gid: String, +} + +#[derive(Debug)] +pub enum LoginResult { + Ok, + BadRSA, + BadCredentials, + NeedCaptcha, + Need2FA, + NeedEmail, + OtherFailure, +} + +#[derive(Debug)] +pub struct UserLogin { + pub username: String, + pub password: String, + pub captcha_text: String, + pub twofactor_code: String, + pub email_code: String, + + cookies: reqwest::cookie::Jar, + // cookies: Arc, + client: reqwest::blocking::Client, +} + +impl UserLogin { + pub fn new(username: String, password: String) -> UserLogin { + return UserLogin { + username, + password, + captcha_text: String::from(""), + twofactor_code: String::from(""), + email_code: String::from(""), + cookies: reqwest::cookie::Jar::default(), + // cookies: Arc::::new(reqwest::cookie::Jar::default()), + client: reqwest::blocking::ClientBuilder::new() + .cookie_store(true) + .build() + .unwrap(), + } + } + + fn update_session(&self) { + let url = "https://steamcommunity.com".parse::().unwrap(); + self.cookies.add_cookie_str("mobileClientVersion=0 (2.1.3)", &url); + self.cookies.add_cookie_str("mobileClient=android", &url); + self.cookies.add_cookie_str("Steam_Language=english", &url); + + let _ = self.client + .get("https://steamcommunity.com/login?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client".parse::().unwrap()) + .header("X-Requested-With", "com.valvesoftware.android.steam.community") + .header(USER_AGENT, "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30") + // .header(COOKIE, "mobileClientVersion=0 (2.1.3)") + // .header(COOKIE, "mobileClient=android") + // .header(COOKIE, "Steam_Language=english") + .header(COOKIE, self.cookies.cookies(&url).unwrap()) + .send(); + } + + pub fn login(&self) -> LoginResult { + let url = "https://steamcommunity.com".parse::().unwrap(); + if self.cookies.cookies(&url) == Option::None { + self.update_session() + } + + let mut params = HashMap::new(); + params.insert("donotcache", format!("{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() * 1000)); + params.insert("username", self.username.clone()); + let resp = self.client + .post("https://steamcommunity.com/login/getrsakey") + .form(¶ms) + .send() + .unwrap(); + + let encrypted_password: String; + match resp.json::() { + Ok(rsa_resp) => { + // println!("rsa: {:?}", rsa_resp); + let rsa_exponent = rsa::BigUint::parse_bytes(rsa_resp.publickey_exp.as_bytes(), 16).unwrap(); + let rsa_modulus = rsa::BigUint::parse_bytes(rsa_resp.publickey_mod.as_bytes(), 16).unwrap(); + let public_key = RSAPublicKey::new(rsa_modulus, rsa_exponent).unwrap(); + // println!("public key: {:?}", public_key); + let mut rng = OsRng; + let padding = rsa::PaddingScheme::new_pkcs1v15_encrypt(); + encrypted_password = base64::encode(public_key.encrypt(&mut rng, padding, self.password.as_bytes()).unwrap()); + println!("encrypted_password: {:?}", encrypted_password); + } + Err(error) => { + println!("rsa error: {:?}", error); + return LoginResult::BadRSA + } + } + + let mut params = HashMap::new(); + params.insert("donotcache", format!("{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() * 1000)); + params.insert("username", self.username.clone()); + + return LoginResult::OtherFailure + } +} + + +pub fn get_server_time() -> i64 { + let client = reqwest::blocking::Client::new(); + let resp = client + .post("https://api.steampowered.com/ITwoFactorService/QueryTime/v0001") + .body("steamid=0") + .send(); + let value: serde_json::Value = resp.unwrap().json().unwrap(); + + // println!("{}", value["response"]); + + return String::from(value["response"]["server_time"].as_str().unwrap()).parse().unwrap(); +} From 4a90c146e51d8a093bbd3268fd18ea19b5aa248a Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 25 Mar 2021 12:43:41 -0400 Subject: [PATCH 03/36] add login request --- src/steamapi.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/src/steamapi.rs b/src/steamapi.rs index fd9e41a..76bac4c 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -3,16 +3,31 @@ use reqwest::{Url, cookie::CookieStore, header::COOKIE, header::USER_AGENT}; use rsa::{PublicKey, RSAPublicKey}; use std::time::{SystemTime, UNIX_EPOCH}; use serde::{Serialize, Deserialize}; +use serde::de::{Visitor}; use rand::rngs::OsRng; +use std::fmt; -#[derive(Debug, Deserialize)] +#[derive(Debug, Clone, Deserialize)] struct LoginResponse { success: bool, + #[serde(default)] login_complete: bool, - oauth_data_string: String, + #[serde(default)] + oauth: String, + #[serde(default)] + captcha_needed: bool, + #[serde(default)] + captcha_gid: String, + #[serde(default)] + emailsteamid: u64, + #[serde(default)] + emailauth_needed: bool, + #[serde(default)] + requires_twofactor: bool, + message: String, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Clone, Deserialize)] struct RsaResponse { success: bool, publickey_exp: String, @@ -29,6 +44,7 @@ pub enum LoginResult { NeedCaptcha, Need2FA, NeedEmail, + TooManyAttempts, OtherFailure, } @@ -36,9 +52,12 @@ pub enum LoginResult { pub struct UserLogin { pub username: String, pub password: String, + pub captcha_required: bool, + pub captcha_gid: String, pub captcha_text: String, pub twofactor_code: String, pub email_code: String, + pub steam_id: u64, cookies: reqwest::cookie::Jar, // cookies: Arc, @@ -50,9 +69,12 @@ impl UserLogin { return UserLogin { username, password, + captcha_required: false, + captcha_gid: String::from("-1"), captcha_text: String::from(""), twofactor_code: String::from(""), email_code: String::from(""), + steam_id: 0, cookies: reqwest::cookie::Jar::default(), // cookies: Arc::::new(reqwest::cookie::Jar::default()), client: reqwest::blocking::ClientBuilder::new() @@ -80,6 +102,10 @@ impl UserLogin { } pub fn login(&self) -> LoginResult { + if self.captcha_required && self.captcha_text.len() == 0 { + return LoginResult::NeedCaptcha; + } + let url = "https://steamcommunity.com".parse::().unwrap(); if self.cookies.cookies(&url) == Option::None { self.update_session() @@ -95,6 +121,7 @@ impl UserLogin { .unwrap(); let encrypted_password: String; + let rsa_timestamp: String; match resp.json::() { Ok(rsa_resp) => { // println!("rsa: {:?}", rsa_resp); @@ -106,6 +133,7 @@ impl UserLogin { let padding = rsa::PaddingScheme::new_pkcs1v15_encrypt(); encrypted_password = base64::encode(public_key.encrypt(&mut rng, padding, self.password.as_bytes()).unwrap()); println!("encrypted_password: {:?}", encrypted_password); + rsa_timestamp = rsa_resp.timestamp; } Err(error) => { println!("rsa error: {:?}", error); @@ -116,8 +144,46 @@ impl UserLogin { let mut params = HashMap::new(); params.insert("donotcache", format!("{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() * 1000)); params.insert("username", self.username.clone()); + params.insert("password", encrypted_password); + params.insert("twofactorcode", self.twofactor_code.clone()); + params.insert("emailauth", self.email_code.clone()); + params.insert("captchagid", self.captcha_gid.clone()); + params.insert("captcha_text", self.captcha_text.clone()); + params.insert("rsatimestamp", rsa_timestamp); + params.insert("remember_login", String::from("true")); + params.insert("oauth_client_id", String::from("DE45CD61")); + params.insert("oauth_scope", String::from("read_profile write_profile read_client write_client")); - return LoginResult::OtherFailure + let login_resp: LoginResponse; + match self.client + .post("https://steamcommunity.com/login/dologin") + .form(¶ms) + .send() { + Ok(resp) => { + // println!("login resp: {:?}", &resp.text()); + match resp.json::() { + Ok(lr) => { + println!("login resp: {:?}", lr); + login_resp = lr; + } + Err(error) => { + println!("login parse error: {:?}", error); + return LoginResult::OtherFailure; + } + } + } + Err(error) => { + println!("login request error: {:?}", error); + return LoginResult::OtherFailure; + } + } + + if login_resp.message.contains("too many login") { + return LoginResult::TooManyAttempts; + } + + + return LoginResult::OtherFailure; } } From 748c9b01d04b13c202f0697a67195b107a46df68 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 25 Mar 2021 13:22:42 -0400 Subject: [PATCH 04/36] add other login results --- src/steamapi.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/steamapi.rs b/src/steamapi.rs index 76bac4c..b359c35 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -182,6 +182,26 @@ impl UserLogin { return LoginResult::TooManyAttempts; } + if login_resp.message.contains("Incorrect login") { + return LoginResult::BadCredentials; + } + + if login_resp.captcha_needed { + return LoginResult::NeedCaptcha; + } + + if login_resp.emailauth_needed { + return LoginResult::NeedEmail; + } + + if login_resp.requires_twofactor { + return LoginResult::Need2FA; + } + + if !login_resp.login_complete { + return LoginResult::BadCredentials; + } + return LoginResult::OtherFailure; } From 8680d8d4c0c30397570ba165283c84ad7452f010 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 25 Mar 2021 13:52:55 -0400 Subject: [PATCH 05/36] add oauth data parsing --- src/steamapi.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/steamapi.rs b/src/steamapi.rs index b359c35..fb9577c 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -202,11 +202,40 @@ impl UserLogin { return LoginResult::BadCredentials; } + let oauth: OAuthData = serde_json::from_str(login_resp.oauth).unwrap(); + let session = self.build_session(oauth); - return LoginResult::OtherFailure; + return LoginResult::Ok; + } + + fn build_session(&self, data: OAuthData) -> Session { + return Session{ + token: data.oauth_token, + steam_id: data.steamid, + steam_login: format!("{}%7C%7C{}", data.steamid, data.wgtoken), + steam_login_secure: format!("{}%7C%7C{}", data.steamid, data.wgtoken_secure), + session_id: todo!(), + web_cookie: todo!(), + }; } } +struct OAuthData { + oauth_token: String, + steamid: u64, + wgtoken: String, + wgtoken_secure: String, + webcookie: String, +} + +pub struct Session { + pub session_id: String, + pub steam_login: String, + pub steam_login_secure: String, + pub web_cookie: String, + pub token: String, + pub steam_id: u64, +} pub fn get_server_time() -> i64 { let client = reqwest::blocking::Client::new(); From 3376da2da2fd031e3b805feefdaef27e90390c44 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 25 Mar 2021 15:26:45 -0400 Subject: [PATCH 06/36] add data to login results --- src/steamapi.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/steamapi.rs b/src/steamapi.rs index fb9577c..e1a021a 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -38,10 +38,10 @@ struct RsaResponse { #[derive(Debug)] pub enum LoginResult { - Ok, + Ok{ session: Session }, BadRSA, BadCredentials, - NeedCaptcha, + NeedCaptcha{ captcha_gid: String }, Need2FA, NeedEmail, TooManyAttempts, @@ -103,7 +103,7 @@ impl UserLogin { pub fn login(&self) -> LoginResult { if self.captcha_required && self.captcha_text.len() == 0 { - return LoginResult::NeedCaptcha; + return LoginResult::NeedCaptcha{captcha_gid: self.captcha_gid}; } let url = "https://steamcommunity.com".parse::().unwrap(); @@ -187,7 +187,8 @@ impl UserLogin { } if login_resp.captcha_needed { - return LoginResult::NeedCaptcha; + self.captcha_gid = login_resp.captcha_gid; + return LoginResult::NeedCaptcha{ captcha_gid: self.captcha_gid }; } if login_resp.emailauth_needed { @@ -205,7 +206,7 @@ impl UserLogin { let oauth: OAuthData = serde_json::from_str(login_resp.oauth).unwrap(); let session = self.build_session(oauth); - return LoginResult::Ok; + return LoginResult::Ok{session}; } fn build_session(&self, data: OAuthData) -> Session { From e05858668000054aa27741dff3de4871e4335c30 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 25 Mar 2021 15:31:50 -0400 Subject: [PATCH 07/36] set steam id when email auth needed --- src/steamapi.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/steamapi.rs b/src/steamapi.rs index e1a021a..a98e8eb 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -192,6 +192,7 @@ impl UserLogin { } if login_resp.emailauth_needed { + self.steam_id = login_resp.emailsteamid; return LoginResult::NeedEmail; } From 196e4ffdb8d5631cc7961e8d3de30330b3606488 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 25 Mar 2021 17:45:41 -0400 Subject: [PATCH 08/36] add manifest and manifest entry types --- src/accountmanager.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/accountmanager.rs diff --git a/src/accountmanager.rs b/src/accountmanager.rs new file mode 100644 index 0000000..60466eb --- /dev/null +++ b/src/accountmanager.rs @@ -0,0 +1,26 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Manifest { + pub encrypted: bool, + pub entries: Vec, + pub first_run: bool, + pub periodic_checking: bool, + pub periodic_checking_interval: i32, + pub periodic_checking_checkall: bool, + pub auto_confirm_market_transactions: bool, + pub auto_confirm_trades: bool, + + #[serde(skip)] + pub accounts: Vec +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ManifestEntry { + pub encryption_iv: String, + pub encryption_salt: String, + pub filename: String, + pub steam_id: u64, +} + + From 05b944c40b9b282b566cff185af586424fe2ba0c Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 25 Mar 2021 19:47:44 -0400 Subject: [PATCH 09/36] add working manifest loading --- Cargo.lock | 5 +++-- Cargo.toml | 1 + src/accountmanager.rs | 34 +++++++++++++++++++++++++++++----- src/main.rs | 35 +++++++++++++++++++++-------------- src/steamapi.rs | 14 ++++++++------ 5 files changed, 62 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 18e16eb..dd57b04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1145,9 +1145,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "standback" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "decebb311175fdaf1bf8a14583716e93163c566db2ead1c1d608c3e1e4313cb8" +checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" dependencies = [ "version_check", ] @@ -1213,6 +1213,7 @@ dependencies = [ "rsa", "serde", "serde_json", + "standback", "text_io", ] diff --git a/Cargo.toml b/Cargo.toml index acd79a2..654ae9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,4 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" rsa = "0.3" rand = "0.7.3" # rsa is not compatible with rand 0.8: https://github.com/RustCrypto/RSA/issues/81 +standback = "0.2.17" # required to fix a compilation error on a transient dependency \ No newline at end of file diff --git a/src/accountmanager.rs b/src/accountmanager.rs index 60466eb..c2cda60 100644 --- a/src/accountmanager.rs +++ b/src/accountmanager.rs @@ -1,4 +1,8 @@ +use std::fs::File; +use std::io::BufReader; +use std::path::Path; use serde::{Serialize, Deserialize}; +use std::error::Error; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Manifest { @@ -11,16 +15,36 @@ pub struct Manifest { pub auto_confirm_market_transactions: bool, pub auto_confirm_trades: bool, - #[serde(skip)] - pub accounts: Vec + // #[serde(skip)] + // pub accounts: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ManifestEntry { - pub encryption_iv: String, - pub encryption_salt: String, + pub encryption_iv: Option, + pub encryption_salt: Option, pub filename: String, + #[serde(rename = "steamid")] pub steam_id: u64, } - +impl Manifest { + pub fn load(path: &Path) -> Result> { + match File::open(path) { + Ok(file) => { + let reader = BufReader::new(file); + match serde_json::from_reader(reader) { + Ok(manifest) => { + return Ok(manifest); + } + Err(e) => { + return Err(Box::new(e)); + } + } + } + Err(e) => { + return Err(Box::new(e)); + } + } + } +} diff --git a/src/main.rs b/src/main.rs index a04b62a..0a52188 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,27 +3,34 @@ use io::Write; use steamguard_cli::*; use ::std::*; use text_io::read; +use std::path::Path; mod steamapi; +mod accountmanager; fn main() { println!("Hello, world!"); - let server_time = steamapi::get_server_time(); - println!("server time: {}", server_time); + // let server_time = steamapi::get_server_time(); + // println!("server time: {}", server_time); - let mut account = SteamGuardAccount::new(); - account.shared_secret = parse_shared_secret(String::from("K5I0Fmm+sN0yF41vIslTVm+0nPE=")); + // let mut account = SteamGuardAccount::new(); + // account.shared_secret = parse_shared_secret(String::from("K5I0Fmm+sN0yF41vIslTVm+0nPE=")); - let code = account.generate_code(server_time); - println!("{}", code); + // let code = account.generate_code(server_time); + // println!("{}", code); - print!("Username: "); - let _ = std::io::stdout().flush(); - let username: String = read!("{}\n"); - let password = rpassword::prompt_password_stdout("Password: ").unwrap(); - // println!("{}:{}", username, password); - let login = steamapi::UserLogin::new(username, password); - let result = login.login(); - println!("result: {:?}", result); + // print!("Username: "); + // let _ = std::io::stdout().flush(); + // let username: String = read!("{}\n"); + // let password = rpassword::prompt_password_stdout("Password: ").unwrap(); + // // println!("{}:{}", username, password); + // let login = steamapi::UserLogin::new(username, password); + // let result = login.login(); + // println!("result: {:?}", result); + + let path = Path::new("test_maFiles/manifest.json"); + let manifest = accountmanager::Manifest::load(path); + + println!("{:?}", manifest); } diff --git a/src/steamapi.rs b/src/steamapi.rs index a98e8eb..88f45ac 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -101,9 +101,9 @@ impl UserLogin { .send(); } - pub fn login(&self) -> LoginResult { + pub fn login(&mut self) -> LoginResult { if self.captcha_required && self.captcha_text.len() == 0 { - return LoginResult::NeedCaptcha{captcha_gid: self.captcha_gid}; + return LoginResult::NeedCaptcha{captcha_gid: self.captcha_gid.clone()}; } let url = "https://steamcommunity.com".parse::().unwrap(); @@ -187,12 +187,12 @@ impl UserLogin { } if login_resp.captcha_needed { - self.captcha_gid = login_resp.captcha_gid; - return LoginResult::NeedCaptcha{ captcha_gid: self.captcha_gid }; + self.captcha_gid = login_resp.captcha_gid.clone(); + return LoginResult::NeedCaptcha{ captcha_gid: self.captcha_gid.clone() }; } if login_resp.emailauth_needed { - self.steam_id = login_resp.emailsteamid; + self.steam_id = login_resp.emailsteamid.clone(); return LoginResult::NeedEmail; } @@ -204,7 +204,7 @@ impl UserLogin { return LoginResult::BadCredentials; } - let oauth: OAuthData = serde_json::from_str(login_resp.oauth).unwrap(); + let oauth: OAuthData = serde_json::from_str(login_resp.oauth.as_str()).unwrap(); let session = self.build_session(oauth); return LoginResult::Ok{session}; @@ -222,6 +222,7 @@ impl UserLogin { } } +#[derive(Debug, Clone, Deserialize)] struct OAuthData { oauth_token: String, steamid: u64, @@ -230,6 +231,7 @@ struct OAuthData { webcookie: String, } +#[derive(Debug, Clone, Deserialize)] pub struct Session { pub session_id: String, pub steam_login: String, From a355cb664491e931984e4965311c682fdd5cfba6 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Fri, 26 Mar 2021 13:14:59 -0400 Subject: [PATCH 10/36] update Manifest struct --- src/accountmanager.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/accountmanager.rs b/src/accountmanager.rs index c2cda60..026d231 100644 --- a/src/accountmanager.rs +++ b/src/accountmanager.rs @@ -3,8 +3,9 @@ use std::io::BufReader; use std::path::Path; use serde::{Serialize, Deserialize}; use std::error::Error; +use steamguard_cli::SteamGuardAccount; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct Manifest { pub encrypted: bool, pub entries: Vec, @@ -15,8 +16,10 @@ pub struct Manifest { pub auto_confirm_market_transactions: bool, pub auto_confirm_trades: bool, - // #[serde(skip)] - // pub accounts: Vec, + #[serde(skip)] + pub accounts: Vec, + #[serde(skip)] + folder: String, // I wanted to use a Path here, but it was too hard to make it work... } #[derive(Debug, Clone, Serialize, Deserialize)] From 4e583530cd87421509d84dfd84054a82ab590732 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Fri, 26 Mar 2021 13:32:37 -0400 Subject: [PATCH 11/36] update SteamGuardAccount struct to make it easier to serialize --- src/lib.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f5b8c6d..3356156 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,7 @@ use std::convert::TryInto; +use serde::{Serialize, Deserialize}; + +mod steamapi; // const STEAMAPI_BASE: String = "https://api.steampowered.com"; // const COMMUNITY_BASE: String = "https://steamcommunity.com"; @@ -10,11 +13,13 @@ use std::convert::TryInto; extern crate hmacsha1; extern crate base64; -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct SteamGuardAccount { pub account_name: String, pub revocation_code: String, - pub shared_secret: [u8; 20], + pub shared_secret: String, + #[serde(rename = "Session")] + pub session: Option, } fn build_time_bytes(mut time: i64) -> [u8; 8] { @@ -47,7 +52,8 @@ impl SteamGuardAccount { return SteamGuardAccount{ account_name: String::from(""), revocation_code: String::from(""), - shared_secret: [0; 20], + shared_secret: String::from(""), + session: Option::None, } } @@ -55,8 +61,9 @@ impl SteamGuardAccount { let steam_guard_code_translations: [u8; 26] = [50, 51, 52, 53, 54, 55, 56, 57, 66, 67, 68, 70, 71, 72, 74, 75, 77, 78, 80, 81, 82, 84, 86, 87, 88, 89]; let time_bytes: [u8; 8] = build_time_bytes(time); + let shared_secret: [u8; 20] = parse_shared_secret(self.shared_secret.clone()); // println!("time_bytes: {:?}", time_bytes); - let hashed_data = hmacsha1::hmac_sha1(&self.shared_secret, &time_bytes); + let hashed_data = hmacsha1::hmac_sha1(&shared_secret, &time_bytes); // println!("hashed_data: {:?}", hashed_data); let mut code_array: [u8; 5] = [0; 5]; let b = (hashed_data[19] & 0xF) as usize; @@ -84,7 +91,7 @@ mod tests { #[test] fn test_generate_code() { let mut account = SteamGuardAccount::new(); - account.shared_secret = parse_shared_secret(String::from("zvIayp3JPvtvX/QGHqsqKBk/44s=")); + account.shared_secret = String::from("zvIayp3JPvtvX/QGHqsqKBk/44s="); let code = account.generate_code(1616374841i64); assert_eq!(code, "2F9J5") From d7b952b40fee3855f41c62f8f5e37980f9825ea7 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Fri, 26 Mar 2021 14:05:54 -0400 Subject: [PATCH 12/36] add load_accounts --- src/accountmanager.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/accountmanager.rs b/src/accountmanager.rs index 026d231..fd8ae2b 100644 --- a/src/accountmanager.rs +++ b/src/accountmanager.rs @@ -37,7 +37,9 @@ impl Manifest { Ok(file) => { let reader = BufReader::new(file); match serde_json::from_reader(reader) { - Ok(manifest) => { + Ok(m) => { + let mut manifest: Manifest = m; + manifest.folder = String::from(path.parent().unwrap().to_str().unwrap()); return Ok(manifest); } Err(e) => { @@ -50,4 +52,27 @@ impl Manifest { } } } + + pub fn load_accounts(&mut self) { + for entry in &self.entries { + let path = Path::new(&self.folder).join(&entry.filename); + match File::open(path) { + Ok(f) => { + let reader = BufReader::new(f); + match serde_json::from_reader(reader) { + Ok(a) => { + let account: SteamGuardAccount = a; + self.accounts.push(account); + } + Err(e) => { + panic!("invalid json") + } + } + } + Err(e) => { + panic!(e) + } + } + } + } } From 347322ce26f3ed9d2ed25a1bb474c04c6190a9e4 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Fri, 26 Mar 2021 14:31:48 -0400 Subject: [PATCH 13/36] add the rest of the fields to SteamGuardAccount --- src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 3356156..e526566 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,8 +16,14 @@ extern crate base64; #[derive(Debug, Serialize, Deserialize)] pub struct SteamGuardAccount { pub account_name: String, + pub serial_number: String, pub revocation_code: String, pub shared_secret: String, + pub token_gid: String, + pub identity_secret: String, + pub server_time: u64, + pub uri: String, + pub fully_enrolled: bool, #[serde(rename = "Session")] pub session: Option, } @@ -51,8 +57,14 @@ impl SteamGuardAccount { pub fn new() -> Self { return SteamGuardAccount{ account_name: String::from(""), + serial_number: String::from(""), revocation_code: String::from(""), shared_secret: String::from(""), + token_gid: String::from(""), + identity_secret: String::from(""), + server_time: 0, + uri: String::from(""), + fully_enrolled: false, session: Option::None, } } From e9d9b610245e86b255b3a6b7a921fa71d6bdaab6 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 27 Mar 2021 08:17:56 -0400 Subject: [PATCH 14/36] add session serialization --- src/steamapi.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/steamapi.rs b/src/steamapi.rs index 88f45ac..09f474a 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -231,13 +231,19 @@ struct OAuthData { webcookie: String, } -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Session { + #[serde(rename = "SessionID")] pub session_id: String, + #[serde(rename = "SteamLogin")] pub steam_login: String, + #[serde(rename = "SteamLoginSecure")] pub steam_login_secure: String, + #[serde(rename = "WebCookie")] pub web_cookie: String, + #[serde(rename = "OAuthToken")] pub token: String, + #[serde(rename = "SteamID")] pub steam_id: u64, } From 52b40ab582e66e523b02f9977e752698a2ea52b3 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 27 Mar 2021 08:39:26 -0400 Subject: [PATCH 15/36] load manifest and generate code --- src/main.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 0a52188..96a7f54 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,19 @@ fn main() { let path = Path::new("test_maFiles/manifest.json"); let manifest = accountmanager::Manifest::load(path); - println!("{:?}", manifest); + match manifest { + Ok(mut m) => { + m.load_accounts(); + for account in m.accounts { + println!("{:?}", account); + let server_time = steamapi::get_server_time(); + let code = account.generate_code(server_time); + println!("{}", code); + } + } + Err(e) => { + println!("{}", e) + } + } } From bde8a37d3f07e868e3d37a754eee8ae88368aa0a Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 27 Mar 2021 09:31:38 -0400 Subject: [PATCH 16/36] add argument parsing --- Cargo.lock | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 ++- src/main.rs | 43 ++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index dd57b04..f90ba64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + [[package]] name = "async-compression" version = "0.3.7" @@ -19,6 +28,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "0.1.7" @@ -101,6 +121,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "const_fn" version = "0.4.5" @@ -1206,6 +1241,7 @@ name = "steamguard-cli" version = "0.2.0" dependencies = [ "base64", + "clap", "hmac-sha1", "rand 0.7.3", "reqwest", @@ -1217,6 +1253,12 @@ dependencies = [ "text_io", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "subtle" version = "2.4.0" @@ -1266,6 +1308,15 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cb170b4f47dc48835fbc56259c12d8963e542b05a24be2e3a1f5a6c320fd2d4" +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.24" @@ -1444,6 +1495,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.2.1" @@ -1468,6 +1525,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index 654ae9c..4bd1ec1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,5 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" rsa = "0.3" rand = "0.7.3" # rsa is not compatible with rand 0.8: https://github.com/RustCrypto/RSA/issues/81 -standback = "0.2.17" # required to fix a compilation error on a transient dependency \ No newline at end of file +standback = "0.2.17" # required to fix a compilation error on a transient dependency +clap = "2.33.3" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 96a7f54..e3bcee3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,11 +4,54 @@ use steamguard_cli::*; use ::std::*; use text_io::read; use std::path::Path; +use clap::{App, Arg, crate_version}; mod steamapi; mod accountmanager; fn main() { + let matches = App::new("steamguard-cli") + .version(crate_version!()) + .bin_name("steamguard") + .author("dyc3 (Carson McManus)") + .about("Generate Steam 2FA codes and confirm Steam trades from the command line.") + .arg( + Arg::with_name("username") + .long("username") + .short("u") + .help("Select the account you want by steam username. By default, the first account in the manifest is selected.") + ) + .arg( + Arg::with_name("all") + .long("all") + .short("a") + .help("Select all accounts in the manifest.") + ) + .arg( + Arg::with_name("mafiles-path") + .long("mafiles-path") + .short("m") + .default_value("~/maFiles") + .help("Specify which folder your maFiles are in.") + ) + .arg( + Arg::with_name("passkey") + .long("passkey") + .short("p") + .help("Specify your encryption passkey.") + ) + .subcommand( + App::new("trade") + .about("Interactive interface for trade confirmations") + .arg( + Arg::with_name("accept-all") + .short("a") + .long("accept-all") + .help("Accept all open trade confirmations. Does not open interactive interface.") + ) + ) + .get_matches(); + println!("Hello, world!"); // let server_time = steamapi::get_server_time(); From 8ded30e09e187c4b0a83c7a070f1c0b0b966d642 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 27 Mar 2021 10:35:52 -0400 Subject: [PATCH 17/36] add logging --- Cargo.lock | 111 ++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 4 +- src/accountmanager.rs | 7 ++- src/main.rs | 58 ++++++++++------------ 4 files changed, 136 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f90ba64..a9bfa56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,12 +30,12 @@ dependencies = [ [[package]] name = "atty" -version = "0.2.14" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" dependencies = [ - "hermit-abi", "libc", + "termion", "winapi", ] @@ -503,6 +503,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" + [[package]] name = "lazy_static" version = "1.4.0" @@ -590,7 +596,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" dependencies = [ - "lazy_static", + "lazy_static 1.4.0", "libc", "log", "openssl", @@ -630,7 +636,7 @@ checksum = "5d51546d704f52ef14b3c962b5776e53d5b862e5790e40a350d366c209bd7f7a" dependencies = [ "autocfg 0.1.7", "byteorder", - "lazy_static", + "lazy_static 1.4.0", "libm", "num-integer", "num-iter", @@ -681,6 +687,12 @@ dependencies = [ "libc", ] +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" + [[package]] name = "once_cell" version = "1.7.2" @@ -911,6 +923,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_termios" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" +dependencies = [ + "redox_syscall", +] + [[package]] name = "regex" version = "1.4.5" @@ -955,7 +976,7 @@ dependencies = [ "hyper-tls", "ipnet", "js-sys", - "lazy_static", + "lazy_static 1.4.0", "log", "mime", "native-tls", @@ -993,7 +1014,7 @@ checksum = "3648b669b10afeab18972c105e284a7b953a669b0be3514c27f9b17acab2f9cd" dependencies = [ "byteorder", "digest", - "lazy_static", + "lazy_static 1.4.0", "num-bigint-dig", "num-integer", "num-iter", @@ -1028,7 +1049,7 @@ version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ - "lazy_static", + "lazy_static 1.4.0", "winapi", ] @@ -1187,6 +1208,19 @@ dependencies = [ "version_check", ] +[[package]] +name = "stderrlog" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e5ee9b90a5452c570a0b0ac1c99ae9498db7e56e33d74366de7f2a7add7f25" +dependencies = [ + "atty", + "chrono", + "log", + "termcolor", + "thread_local", +] + [[package]] name = "stdweb" version = "0.4.20" @@ -1243,6 +1277,7 @@ dependencies = [ "base64", "clap", "hmac-sha1", + "log", "rand 0.7.3", "reqwest", "rpassword", @@ -1250,6 +1285,7 @@ dependencies = [ "serde", "serde_json", "standback", + "stderrlog", "text_io", ] @@ -1302,6 +1338,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termion" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" +dependencies = [ + "libc", + "numtoa", + "redox_syscall", + "redox_termios", +] + [[package]] name = "text_io" version = "0.1.8" @@ -1337,6 +1394,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" +dependencies = [ + "lazy_static 0.2.11", + "unreachable", +] + [[package]] name = "time" version = "0.1.43" @@ -1462,7 +1529,7 @@ version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" dependencies = [ - "lazy_static", + "lazy_static 1.4.0", ] [[package]] @@ -1507,6 +1574,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + [[package]] name = "url" version = "2.2.1" @@ -1537,6 +1613,12 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "want" version = "0.3.0" @@ -1578,7 +1660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3" dependencies = [ "bumpalo", - "lazy_static", + "lazy_static 1.4.0", "log", "proc-macro2", "quote", @@ -1653,6 +1735,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 4bd1ec1..fa1c1ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,6 @@ serde_json = "1.0" rsa = "0.3" rand = "0.7.3" # rsa is not compatible with rand 0.8: https://github.com/RustCrypto/RSA/issues/81 standback = "0.2.17" # required to fix a compilation error on a transient dependency -clap = "2.33.3" \ No newline at end of file +clap = "2.33.3" +log = "0.4.14" +stderrlog = "0.4" diff --git a/src/accountmanager.rs b/src/accountmanager.rs index fd8ae2b..8c36dea 100644 --- a/src/accountmanager.rs +++ b/src/accountmanager.rs @@ -4,6 +4,7 @@ use std::path::Path; use serde::{Serialize, Deserialize}; use std::error::Error; use steamguard_cli::SteamGuardAccount; +use log::*; #[derive(Debug, Serialize, Deserialize)] pub struct Manifest { @@ -33,6 +34,7 @@ pub struct ManifestEntry { impl Manifest { pub fn load(path: &Path) -> Result> { + debug!("loading manifest: {:?}", &path); match File::open(path) { Ok(file) => { let reader = BufReader::new(file); @@ -56,6 +58,7 @@ impl Manifest { pub fn load_accounts(&mut self) { for entry in &self.entries { let path = Path::new(&self.folder).join(&entry.filename); + debug!("loading account: {:?}", path); match File::open(path) { Ok(f) => { let reader = BufReader::new(f); @@ -65,12 +68,12 @@ impl Manifest { self.accounts.push(account); } Err(e) => { - panic!("invalid json") + error!("invalid json: {}", e) } } } Err(e) => { - panic!(e) + error!("unable to open account: {}", e) } } } diff --git a/src/main.rs b/src/main.rs index e3bcee3..ec703d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use ::std::*; use text_io::read; use std::path::Path; use clap::{App, Arg, crate_version}; +use log::*; mod steamapi; mod accountmanager; @@ -40,6 +41,13 @@ fn main() { .short("p") .help("Specify your encryption passkey.") ) + .arg( + Arg::with_name("verbosity") + .short("v") + .help("Log what is going on verbosely.") + .takes_value(false) + .multiple(true) + ) .subcommand( App::new("trade") .about("Interactive interface for trade confirmations") @@ -52,41 +60,29 @@ fn main() { ) .get_matches(); - println!("Hello, world!"); - // let server_time = steamapi::get_server_time(); - // println!("server time: {}", server_time); + let verbosity = matches.occurrences_of("verbosity") as usize + 2; + stderrlog::new() + .verbosity(verbosity) + .module(module_path!()).init().unwrap(); - // let mut account = SteamGuardAccount::new(); - // account.shared_secret = parse_shared_secret(String::from("K5I0Fmm+sN0yF41vIslTVm+0nPE=")); - - // let code = account.generate_code(server_time); - // println!("{}", code); - - // print!("Username: "); - // let _ = std::io::stdout().flush(); - // let username: String = read!("{}\n"); - // let password = rpassword::prompt_password_stdout("Password: ").unwrap(); - // // println!("{}:{}", username, password); - // let login = steamapi::UserLogin::new(username, password); - // let result = login.login(); - // println!("result: {:?}", result); - - let path = Path::new("test_maFiles/manifest.json"); - let manifest = accountmanager::Manifest::load(path); - println!("{:?}", manifest); - match manifest { - Ok(mut m) => { - m.load_accounts(); - for account in m.accounts { - println!("{:?}", account); - let server_time = steamapi::get_server_time(); - let code = account.generate_code(server_time); - println!("{}", code); - } + let path = Path::new(matches.value_of("mafiles-path").unwrap()).join("manifest.json"); + let mut manifest: accountmanager::Manifest; + match accountmanager::Manifest::load(path.as_path()) { + Ok(m) => { + manifest = m; } Err(e) => { - println!("{}", e) + error!("Could not load manifest: {}", e); + return; } } + + manifest.load_accounts(); + for account in manifest.accounts { + trace!("{:?}", account); + let server_time = steamapi::get_server_time(); + let code = account.generate_code(server_time); + println!("{}", code); + } } From 23257ee615959548d268098ebebdc8a32800d80c Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 27 Mar 2021 12:14:34 -0400 Subject: [PATCH 18/36] add account selection from arguments --- src/lib.rs | 2 +- src/main.rs | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e526566..5639dcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ mod steamapi; extern crate hmacsha1; extern crate base64; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SteamGuardAccount { pub account_name: String, pub serial_number: String, diff --git a/src/main.rs b/src/main.rs index ec703d5..b8cafee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ extern crate rpassword; +use borrow::BorrowMut; use io::Write; use steamguard_cli::*; use ::std::*; @@ -26,6 +27,7 @@ fn main() { Arg::with_name("all") .long("all") .short("a") + .takes_value(false) .help("Select all accounts in the manifest.") ) .arg( @@ -55,6 +57,7 @@ fn main() { Arg::with_name("accept-all") .short("a") .long("accept-all") + .takes_value(false) .help("Accept all open trade confirmations. Does not open interactive interface.") ) ) @@ -79,9 +82,30 @@ fn main() { } manifest.load_accounts(); - for account in manifest.accounts { + let mut selected_accounts: Vec = vec![]; + if matches.is_present("all") { + // manifest.accounts.iter().map(|a| selected_accounts.push(a.b)); + for account in manifest.accounts { + selected_accounts.push(account.clone()); + } + } else { + for account in manifest.accounts { + if !matches.is_present("username") { + selected_accounts.push(account.clone()); + break; + } + if matches.value_of("username").unwrap() == account.account_name { + selected_accounts.push(account.clone()); + break; + } + } + } + + debug!("selected accounts: {:?}", selected_accounts.iter().map(|a| a.account_name.clone()).collect::>()); + + let server_time = steamapi::get_server_time(); + for account in selected_accounts { trace!("{:?}", account); - let server_time = steamapi::get_server_time(); let code = account.generate_code(server_time); println!("{}", code); } From fe6a51030f23d643aad67d9d386e8372028dad71 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 30 Mar 2021 15:51:26 -0400 Subject: [PATCH 19/36] add working login process --- Cargo.lock | 1 + Cargo.toml | 1 + src/lib.rs | 3 +- src/main.rs | 58 ++++++++++++++++++++--- src/steamapi.rs | 120 +++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 158 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9bfa56..0c30a92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1276,6 +1276,7 @@ version = "0.2.0" dependencies = [ "base64", "clap", + "cookie", "hmac-sha1", "log", "rand 0.7.3", diff --git a/Cargo.toml b/Cargo.toml index fa1c1ed..1c92f00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ standback = "0.2.17" # required to fix a compilation error on a transient depend clap = "2.33.3" log = "0.4.14" stderrlog = "0.4" +cookie = "0.14" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 5639dcb..44a205d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; use serde::{Serialize, Deserialize}; -mod steamapi; +pub mod steamapi; // const STEAMAPI_BASE: String = "https://api.steampowered.com"; // const COMMUNITY_BASE: String = "https://steamcommunity.com"; @@ -12,6 +12,7 @@ mod steamapi; extern crate hmacsha1; extern crate base64; +extern crate cookie; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SteamGuardAccount { diff --git a/src/main.rs b/src/main.rs index b8cafee..2bf7370 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,11 +4,10 @@ use io::Write; use steamguard_cli::*; use ::std::*; use text_io::read; -use std::path::Path; +use std::{io::stdin, path::Path}; use clap::{App, Arg, crate_version}; use log::*; -mod steamapi; mod accountmanager; fn main() { @@ -103,10 +102,55 @@ fn main() { debug!("selected accounts: {:?}", selected_accounts.iter().map(|a| a.account_name.clone()).collect::>()); - let server_time = steamapi::get_server_time(); - for account in selected_accounts { - trace!("{:?}", account); - let code = account.generate_code(server_time); - println!("{}", code); + if matches.is_present("trade") { + info!("trade"); + for account in selected_accounts.iter_mut() { + let _ = std::io::stdout().flush(); + let password = rpassword::prompt_password_stdout("Password: ").unwrap(); + trace!("password: {}", password); + let mut login = steamapi::UserLogin::new(account.account_name.clone(), password); + let mut loops = 0; + loop { + match login.login() { + steamapi::LoginResult::Ok(s) => { + account.session = Option::Some(s); + break; + } + steamapi::LoginResult::Need2FA => { + let server_time = steamapi::get_server_time(); + login.twofactor_code = account.generate_code(server_time); + } + steamapi::LoginResult::NeedCaptcha{ captcha_gid } => { + // example captchas: + // - 3982844815370620954 + // - 3982844815370767244 + // - 3982844815370804220 + // - 3982844815370819607 + println!("Captcha required. Open this link in your web browser: https://steamcommunity.com/public/captcha.php?gid={}", captcha_gid); + print!("Enter captcha text: "); + let _ = std::io::stdout().flush(); + let mut captcha_text = String::new(); + stdin().read_line(&mut captcha_text).expect("Did not enter a correct string"); + login.captcha_text = String::from(captcha_text.strip_suffix('\n').unwrap()); + } + r => { + error!("Fatal login result: {:?}", r); + return; + } + } + loops += 1; + if loops > 2 { + error!("Too many loops. Aborting login process, to avoid getting rate limited."); + return; + } + } + } + } else { + let server_time = steamapi::get_server_time(); + for account in selected_accounts { + trace!("{:?}", account); + let code = account.generate_code(server_time); + println!("{}", code); + } } } diff --git a/src/steamapi.rs b/src/steamapi.rs index 09f474a..5100de1 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -1,19 +1,20 @@ use std::collections::HashMap; -use reqwest::{Url, cookie::CookieStore, header::COOKIE, header::USER_AGENT}; +use reqwest::{Url, cookie::{CookieStore}, header::COOKIE, header::{SET_COOKIE, USER_AGENT}}; use rsa::{PublicKey, RSAPublicKey}; use std::time::{SystemTime, UNIX_EPOCH}; use serde::{Serialize, Deserialize}; use serde::de::{Visitor}; use rand::rngs::OsRng; use std::fmt; +use log::*; #[derive(Debug, Clone, Deserialize)] struct LoginResponse { success: bool, #[serde(default)] login_complete: bool, - #[serde(default)] - oauth: String, + // #[serde(default)] + // oauth: String, #[serde(default)] captcha_needed: bool, #[serde(default)] @@ -24,7 +25,19 @@ struct LoginResponse { emailauth_needed: bool, #[serde(default)] requires_twofactor: bool, + #[serde(default)] message: String, + transfer_urls: Option>, + transfer_parameters: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct LoginTransferParameters { + steamid: String, + token_secure: String, + auth: String, + remember_login: bool, + webcookie: String, } #[derive(Debug, Clone, Deserialize)] @@ -38,7 +51,7 @@ struct RsaResponse { #[derive(Debug)] pub enum LoginResult { - Ok{ session: Session }, + Ok(Session), BadRSA, BadCredentials, NeedCaptcha{ captcha_gid: String }, @@ -85,6 +98,7 @@ impl UserLogin { } fn update_session(&self) { + trace!("UserLogin::update_session"); let url = "https://steamcommunity.com".parse::().unwrap(); self.cookies.add_cookie_str("mobileClientVersion=0 (2.1.3)", &url); self.cookies.add_cookie_str("mobileClient=android", &url); @@ -99,9 +113,12 @@ impl UserLogin { // .header(COOKIE, "Steam_Language=english") .header(COOKIE, self.cookies.cookies(&url).unwrap()) .send(); + + trace!("cookies: {:?}", self.cookies); } pub fn login(&mut self) -> LoginResult { + trace!("UserLogin::login"); if self.captcha_required && self.captcha_text.len() == 0 { return LoginResult::NeedCaptcha{captcha_gid: self.captcha_gid.clone()}; } @@ -132,15 +149,19 @@ impl UserLogin { let mut rng = OsRng; let padding = rsa::PaddingScheme::new_pkcs1v15_encrypt(); encrypted_password = base64::encode(public_key.encrypt(&mut rng, padding, self.password.as_bytes()).unwrap()); - println!("encrypted_password: {:?}", encrypted_password); + // trace!("encrypted_password: {:?}", encrypted_password); rsa_timestamp = rsa_resp.timestamp; } Err(error) => { - println!("rsa error: {:?}", error); + error!("rsa error: {:?}", error); return LoginResult::BadRSA } } + debug!("captchagid: {}", self.captcha_gid); + debug!("captcha_text: {}", self.captcha_text); + debug!("twofactorcode: {}", self.twofactor_code); + debug!("emailauth: {}", self.email_code); let mut params = HashMap::new(); params.insert("donotcache", format!("{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() * 1000)); params.insert("username", self.username.clone()); @@ -160,20 +181,23 @@ impl UserLogin { .form(¶ms) .send() { Ok(resp) => { - // println!("login resp: {:?}", &resp.text()); - match resp.json::() { + // https://stackoverflow.com/questions/49928648/rubys-mechanize-error-401-while-sending-a-post-request-steam-trade-offer-send + let text = resp.text().unwrap(); + trace!("resp content: {}", text); + match serde_json::from_str(text.as_str()) { Ok(lr) => { - println!("login resp: {:?}", lr); + info!("login resp: {:?}", lr); login_resp = lr; } Err(error) => { - println!("login parse error: {:?}", error); + debug!("login response did not have normal schema"); + error!("login parse error: {:?}", error); return LoginResult::OtherFailure; } } } Err(error) => { - println!("login request error: {:?}", error); + error!("login request error: {:?}", error); return LoginResult::OtherFailure; } } @@ -204,22 +228,84 @@ impl UserLogin { return LoginResult::BadCredentials; } - let oauth: OAuthData = serde_json::from_str(login_resp.oauth.as_str()).unwrap(); - let session = self.build_session(oauth); - return LoginResult::Ok{session}; + // transfer login parameters? Not completely sure what this is for. + // i guess steam changed their authentication scheme slightly + let oauth; + match (login_resp.transfer_urls, login_resp.transfer_parameters) { + (Some(urls), Some(params)) => { + debug!("received transfer parameters, relaying data..."); + for url in urls { + trace!("posting transfer to {}", url); + let result = self.client + .post(url) + .json(¶ms) + .send(); + trace!("result: {:?}", result); + match result { + Ok(resp) => { + debug!("result status: {}", resp.status()); + self.save_cookies_from_response(&resp); + } + Err(e) => { + error!("failed to transfer parameters: {:?}", e); + } + } + } + + oauth = OAuthData { + oauth_token: params.auth, + steamid: params.steamid.parse().unwrap(), + wgtoken: params.token_secure.clone(), // guessing + wgtoken_secure: params.token_secure, + webcookie: params.webcookie, + }; + } + _ => { + error!("did not receive transfer_urls and transfer_parameters"); + return LoginResult::OtherFailure; + } + } + + // let oauth: OAuthData = serde_json::from_str(login_resp.oauth.as_str()).unwrap(); + let url = "https://steamcommunity.com".parse::().unwrap(); + let cookies = self.cookies.cookies(&url).unwrap(); + let all_cookies = cookies.to_str().unwrap(); + let mut session_id = String::from(""); + for cookie in all_cookies.split(";").map(|s| cookie::Cookie::parse(s).unwrap()) { + if cookie.name() == "sessionid" { + session_id = String::from(cookie.value()); + } + } + trace!("cookies {:?}", cookies); + let session = self.build_session(oauth, session_id); + + return LoginResult::Ok(session); } - fn build_session(&self, data: OAuthData) -> Session { + fn build_session(&self, data: OAuthData, session_id: String) -> Session { return Session{ token: data.oauth_token, steam_id: data.steamid, steam_login: format!("{}%7C%7C{}", data.steamid, data.wgtoken), steam_login_secure: format!("{}%7C%7C{}", data.steamid, data.wgtoken_secure), - session_id: todo!(), - web_cookie: todo!(), + session_id: session_id, + web_cookie: data.webcookie, }; } + + fn save_cookies_from_response(&mut self, response: &reqwest::blocking::Response) { + let set_cookie_iter = response.headers().get_all(SET_COOKIE); + let url = "https://steamcommunity.com".parse::().unwrap(); + + for c in set_cookie_iter { + c.to_str() + .into_iter() + .for_each(|cookie_str| { + self.cookies.add_cookie_str(cookie_str, &url) + }); + } + } } #[derive(Debug, Clone, Deserialize)] From 747c09c91a14c2d6052576b4d9a0a9aced974f28 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sun, 4 Apr 2021 09:38:34 -0400 Subject: [PATCH 20/36] add test for encrypt_password --- src/steamapi.rs | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/steamapi.rs b/src/steamapi.rs index 5100de1..aa88282 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -141,16 +141,8 @@ impl UserLogin { let rsa_timestamp: String; match resp.json::() { Ok(rsa_resp) => { - // println!("rsa: {:?}", rsa_resp); - let rsa_exponent = rsa::BigUint::parse_bytes(rsa_resp.publickey_exp.as_bytes(), 16).unwrap(); - let rsa_modulus = rsa::BigUint::parse_bytes(rsa_resp.publickey_mod.as_bytes(), 16).unwrap(); - let public_key = RSAPublicKey::new(rsa_modulus, rsa_exponent).unwrap(); - // println!("public key: {:?}", public_key); - let mut rng = OsRng; - let padding = rsa::PaddingScheme::new_pkcs1v15_encrypt(); - encrypted_password = base64::encode(public_key.encrypt(&mut rng, padding, self.password.as_bytes()).unwrap()); - // trace!("encrypted_password: {:?}", encrypted_password); - rsa_timestamp = rsa_resp.timestamp; + rsa_timestamp = rsa_resp.timestamp.clone(); + encrypted_password = encrypt_password(rsa_resp, &self.password); } Err(error) => { error!("rsa error: {:?}", error); @@ -158,10 +150,10 @@ impl UserLogin { } } - debug!("captchagid: {}", self.captcha_gid); - debug!("captcha_text: {}", self.captcha_text); - debug!("twofactorcode: {}", self.twofactor_code); - debug!("emailauth: {}", self.email_code); + trace!("captchagid: {}", self.captcha_gid); + trace!("captcha_text: {}", self.captcha_text); + trace!("twofactorcode: {}", self.twofactor_code); + trace!("emailauth: {}", self.email_code); let mut params = HashMap::new(); params.insert("donotcache", format!("{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() * 1000)); params.insert("username", self.username.clone()); @@ -345,3 +337,26 @@ pub fn get_server_time() -> i64 { return String::from(value["response"]["server_time"].as_str().unwrap()).parse().unwrap(); } + +fn encrypt_password(rsa_resp: RsaResponse, password: &String) -> String { + let rsa_exponent = rsa::BigUint::parse_bytes(rsa_resp.publickey_exp.as_bytes(), 16).unwrap(); + let rsa_modulus = rsa::BigUint::parse_bytes(rsa_resp.publickey_mod.as_bytes(), 16).unwrap(); + let public_key = RSAPublicKey::new(rsa_modulus, rsa_exponent).unwrap(); + let mut rng = OsRng; + let padding = rsa::PaddingScheme::new_pkcs1v15_encrypt(); + let encrypted_password = base64::encode(public_key.encrypt(&mut rng, padding, password.as_bytes()).unwrap()); + return encrypted_password; +} + +#[test] +fn test_encrypt_password() { + let rsa_resp = RsaResponse{ + success: true, + publickey_exp: String::from("010001"), + publickey_mod: String::from("98f9088c1250b17fe19d2b2422d54a1eef0036875301731f11bd17900e215318eb6de1546727c0b7b61b86cefccdcb2f8108c813154d9a7d55631965eece810d4ab9d8a59c486bda778651b876176070598a93c2325c275cb9c17bdbcacf8edc9c18c0c5d59bc35703505ef8a09ed4c62b9f92a3fac5740ce25e490ab0e26d872140e4103d912d1e3958f844264211277ee08d2b4dd3ac58b030b25342bd5c949ae7794e46a8eab26d5a8deca683bfd381da6c305b19868b8c7cd321ce72c693310a6ebf2ecd43642518f825894602f6c239cf193cb4346ce64beac31e20ef88f934f2f776597734bb9eae1ebdf4a453973b6df9d5e90777bffe5db83dd1757b"), + timestamp: String::from("asdf"), + token_gid: String::from("asdf"), + }; + let result = encrypt_password(rsa_resp, &String::from("kelwleofpsm3n4ofc")); + assert_eq!(result.len(), 344); // can't test exact match because the result is different every time (because of OsRng) +} From 71bb8e8adcfb5fc722662f1e13c6d626dff2e31c Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sun, 4 Apr 2021 10:40:16 -0400 Subject: [PATCH 21/36] add a little bit of validation to user input --- Cargo.lock | 13 +++++++++++ Cargo.toml | 4 +++- src/main.rs | 63 +++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 68 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c30a92..8af5142 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -938,6 +947,8 @@ version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -1278,8 +1289,10 @@ dependencies = [ "clap", "cookie", "hmac-sha1", + "lazy_static 1.4.0", "log", "rand 0.7.3", + "regex", "reqwest", "rpassword", "rsa", diff --git a/Cargo.toml b/Cargo.toml index 1c92f00..3e2d098 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,4 +20,6 @@ standback = "0.2.17" # required to fix a compilation error on a transient depend clap = "2.33.3" log = "0.4.14" stderrlog = "0.4" -cookie = "0.14" \ No newline at end of file +cookie = "0.14" +regex = "1" +lazy_static = "1.4.0" diff --git a/src/main.rs b/src/main.rs index 2bf7370..4db61a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,9 +7,16 @@ use text_io::read; use std::{io::stdin, path::Path}; use clap::{App, Arg, crate_version}; use log::*; +use regex::Regex; +#[macro_use] +extern crate lazy_static; mod accountmanager; +lazy_static! { + static ref CAPTCHA_VALID_CHARS: Regex = Regex::new("^([A-H]|[J-N]|[P-R]|[T-Z]|[2-4]|[7-9]|[@%&])+$").unwrap(); +} + fn main() { let matches = App::new("steamguard-cli") .version(crate_version!()) @@ -121,17 +128,7 @@ fn main() { login.twofactor_code = account.generate_code(server_time); } steamapi::LoginResult::NeedCaptcha{ captcha_gid } => { - // example captchas: - // - 3982844815370620954 - // - 3982844815370767244 - // - 3982844815370804220 - // - 3982844815370819607 - println!("Captcha required. Open this link in your web browser: https://steamcommunity.com/public/captcha.php?gid={}", captcha_gid); - print!("Enter captcha text: "); - let _ = std::io::stdout().flush(); - let mut captcha_text = String::new(); - stdin().read_line(&mut captcha_text).expect("Did not enter a correct string"); - login.captcha_text = String::from(captcha_text.strip_suffix('\n').unwrap()); + login.captcha_text = prompt_captcha_text(&captcha_gid); } r => { error!("Fatal login result: {:?}", r); @@ -154,3 +151,47 @@ fn main() { } } } + +fn validate_captcha_text(text: &String) -> bool { + return CAPTCHA_VALID_CHARS.is_match(text); +} + +#[test] +fn test_validate_captcha_text() { + assert!(validate_captcha_text(&String::from("2WWUA@"))); + assert!(validate_captcha_text(&String::from("3G8HT2"))); + assert!(validate_captcha_text(&String::from("3J%@X3"))); + assert!(validate_captcha_text(&String::from("2GCZ4A"))); + assert!(validate_captcha_text(&String::from("3G8HT2"))); + assert!(!validate_captcha_text(&String::from("asd823"))); + assert!(!validate_captcha_text(&String::from("!PQ4RD"))); + assert!(!validate_captcha_text(&String::from("1GQ4XZ"))); + assert!(!validate_captcha_text(&String::from("8GO4XZ"))); + assert!(!validate_captcha_text(&String::from("IPQ4RD"))); + assert!(!validate_captcha_text(&String::from("0PT4RD"))); + assert!(!validate_captcha_text(&String::from("APTSRD"))); + assert!(!validate_captcha_text(&String::from("AP5TRD"))); + assert!(!validate_captcha_text(&String::from("AP6TRD"))); +} + +/// Prompt the user for text input. +fn prompt() -> String { + let mut text = String::new(); + let _ = std::io::stdout().flush(); + stdin().read_line(&mut text).expect("Did not enter a correct string"); + return String::from(text.strip_suffix('\n').unwrap()); +} + +fn prompt_captcha_text(captcha_gid: &String) -> String { + println!("Captcha required. Open this link in your web browser: https://steamcommunity.com/public/captcha.php?gid={}", captcha_gid); + let mut captcha_text; + loop { + print!("Enter captcha text: "); + captcha_text = prompt(); + if captcha_text.len() > 0 && validate_captcha_text(&captcha_text) { + break; + } + warn!("Invalid chars for captcha text found in user's input. Prompting again..."); + } + return captcha_text; +} From 36c4b7c819eb3435f5c130a784d0041c84ab7103 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sun, 4 Apr 2021 14:07:06 -0400 Subject: [PATCH 22/36] started get_trade_confirmations --- src/lib.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++- src/main.rs | 3 +++ src/steamapi.rs | 3 ++- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 44a205d..92dd606 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ -use std::convert::TryInto; +use std::{collections::HashMap, convert::TryInto, thread, time}; +use hmacsha1::hmac_sha1; +use reqwest::{Url, cookie::CookieStore, header::{COOKIE, USER_AGENT}}; use serde::{Serialize, Deserialize}; +use log::*; pub mod steamapi; @@ -25,6 +28,7 @@ pub struct SteamGuardAccount { pub server_time: u64, pub uri: String, pub fully_enrolled: bool, + pub device_id: String, #[serde(rename = "Session")] pub session: Option, } @@ -66,6 +70,7 @@ impl SteamGuardAccount { server_time: 0, uri: String::from(""), fully_enrolled: false, + device_id: String::from(""), session: Option::None, } } @@ -95,6 +100,61 @@ impl SteamGuardAccount { return String::from_utf8(code_array.iter().map(|c| *c).collect()).unwrap() } + + fn get_confirmation_query_params(&self, tag: &str) -> HashMap<&str, String> { + let session = self.session.clone().unwrap(); + let time = steamapi::get_server_time(); + let mut params = HashMap::new(); + params.insert("p", self.device_id.clone()); + params.insert("a", session.steam_id.to_string()); + params.insert("k", self.generate_confirmation_hash_for_time(time, tag)); + params.insert("t", time.to_string()); + params.insert("m", String::from("android")); + params.insert("tag", String::from(tag)); + return params; + } + + fn generate_confirmation_hash_for_time(&self, time: i64, tag: &str) -> String { + let decode: &[u8] = &base64::decode(&self.identity_secret).unwrap(); + let time_bytes = build_time_bytes(time); + let tag_bytes = tag.as_bytes(); + let array = [&time_bytes, tag_bytes].concat(); + let hash = hmac_sha1(decode, &array); + let encoded = base64::encode(hash); + return encoded; + } + + pub fn get_trade_confirmations(&self) { + // uri: "https://steamcommunity.com/mobileconf/conf" + // confirmation details: + let url = "https://steamcommunity.com".parse::().unwrap(); + let cookies = reqwest::cookie::Jar::default(); + let session_id = self.session.clone().unwrap().session_id; + let cookie_val = format!("sessionid={}", session_id); + cookies.add_cookie_str(cookie_val.as_str(), &url); + let client = reqwest::blocking::ClientBuilder::new() + .build() + .unwrap(); + + loop { + match client + .get("https://steamcommunity.com/mobileconf/conf".parse::().unwrap()) + .header("X-Requested-With", "com.valvesoftware.android.steam.community") + .header(USER_AGENT, "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30") + .header(COOKIE, cookies.cookies(&url).unwrap()) + .query(&self.get_confirmation_query_params("conf")) + .send() { + Ok(resp) => { + info!("{:?}", resp); + break; + } + Err(e) => { + error!("error: {:?}", e); + thread::sleep(time::Duration::from_secs(3)); + } + } + } + } } #[cfg(test)] diff --git a/src/main.rs b/src/main.rs index 4db61a6..2f29419 100644 --- a/src/main.rs +++ b/src/main.rs @@ -141,6 +141,9 @@ fn main() { return; } } + + info!("Checking for trade confirmations"); + account.get_trade_confirmations(); } } else { let server_time = steamapi::get_server_time(); diff --git a/src/steamapi.rs b/src/steamapi.rs index aa88282..77da21d 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -104,7 +104,7 @@ impl UserLogin { self.cookies.add_cookie_str("mobileClient=android", &url); self.cookies.add_cookie_str("Steam_Language=english", &url); - let _ = self.client + let resp = self.client .get("https://steamcommunity.com/login?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client".parse::().unwrap()) .header("X-Requested-With", "com.valvesoftware.android.steam.community") .header(USER_AGENT, "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30") @@ -113,6 +113,7 @@ impl UserLogin { // .header(COOKIE, "Steam_Language=english") .header(COOKIE, self.cookies.cookies(&url).unwrap()) .send(); + trace!("{:?}", resp); trace!("cookies: {:?}", self.cookies); } From d64bf5cadefc8f222061a9d728764fa7fbfb8164 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sun, 4 Apr 2021 17:48:44 -0400 Subject: [PATCH 23/36] can request trade confirmations, don't know if parsing works --- src/accountlinker.rs | 0 src/lib.rs | 65 +++++++++++++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 19 deletions(-) create mode 100644 src/accountlinker.rs diff --git a/src/accountlinker.rs b/src/accountlinker.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs index 92dd606..0430bec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,11 @@ use std::{collections::HashMap, convert::TryInto, thread, time}; use hmacsha1::hmac_sha1; +use regex::Regex; use reqwest::{Url, cookie::CookieStore, header::{COOKIE, USER_AGENT}}; use serde::{Serialize, Deserialize}; use log::*; +#[macro_use] +extern crate lazy_static; pub mod steamapi; @@ -13,6 +16,11 @@ pub mod steamapi; // const TWO_FACTOR_BASE: String = STEAMAPI_BASE + "/ITwoFactorService/%s/v0001"; // static TWO_FACTOR_TIME_QUERY: String = TWO_FACTOR_BASE.Replace("%s", "QueryTime"); +lazy_static! { + static ref CONFIRMATION_REGEX: Regex = Regex::new("
((Confirm|Trade|Account recovery|Sell -) .+)
").unwrap(); +} + extern crate hmacsha1; extern crate base64; extern crate cookie; @@ -129,31 +137,50 @@ impl SteamGuardAccount { // confirmation details: let url = "https://steamcommunity.com".parse::().unwrap(); let cookies = reqwest::cookie::Jar::default(); - let session_id = self.session.clone().unwrap().session_id; - let cookie_val = format!("sessionid={}", session_id); - cookies.add_cookie_str(cookie_val.as_str(), &url); + let session = self.session.clone().unwrap(); + let session_id = session.session_id; + cookies.add_cookie_str("mobileClientVersion=0 (2.1.3)", &url); + cookies.add_cookie_str("mobileClient=android", &url); + cookies.add_cookie_str("Steam_Language=english", &url); + cookies.add_cookie_str("dob=", &url); + cookies.add_cookie_str(format!("sessionid={}", session_id).as_str(), &url); + cookies.add_cookie_str(format!("steamid={}", session.steam_id).as_str(), &url); + cookies.add_cookie_str(format!("steamLogin={}", session.steam_login).as_str(), &url); + cookies.add_cookie_str(format!("steamLoginSecure={}", session.steam_login_secure).as_str(), &url); let client = reqwest::blocking::ClientBuilder::new() + .cookie_store(true) .build() .unwrap(); - loop { - match client - .get("https://steamcommunity.com/mobileconf/conf".parse::().unwrap()) - .header("X-Requested-With", "com.valvesoftware.android.steam.community") - .header(USER_AGENT, "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30") - .header(COOKIE, cookies.cookies(&url).unwrap()) - .query(&self.get_confirmation_query_params("conf")) - .send() { - Ok(resp) => { - info!("{:?}", resp); - break; - } - Err(e) => { - error!("error: {:?}", e); - thread::sleep(time::Duration::from_secs(3)); + match client + .get("https://steamcommunity.com/mobileconf/conf".parse::().unwrap()) + .header("X-Requested-With", "com.valvesoftware.android.steam.community") + .header(USER_AGENT, "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30") + .header(COOKIE, cookies.cookies(&url).unwrap()) + .query(&self.get_confirmation_query_params("conf")) + .send() { + Ok(resp) => { + trace!("{:?}", resp); + let text = resp.text().unwrap(); + trace!("text: {:?}", text); + match CONFIRMATION_REGEX.captures(text.as_str()) { + Some(caps) => { + let conf_id = &caps[1]; + let conf_key = &caps[2]; + let conf_type = &caps[3]; + let conf_creator = &caps[4]; + debug!("{} {} {} {}", conf_id, conf_key, conf_type, conf_creator); + } + _ => { + info!("No confirmations"); + } } } - } + Err(e) => { + error!("error: {:?}", e); + return; + } + } } } From b313b857c8d32945045ae46c47f52c4e9b4ac410 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sun, 4 Apr 2021 23:18:27 -0400 Subject: [PATCH 24/36] fixed generate_confirmation_hash_for_time --- src/lib.rs | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0430bec..d5531c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,8 +42,6 @@ pub struct SteamGuardAccount { } fn build_time_bytes(mut time: i64) -> [u8; 8] { - time /= 30i64; - let mut bytes: [u8; 8] = [0; 8]; for i in (0..8).rev() { bytes[i] = time as u8; @@ -66,6 +64,16 @@ pub fn parse_shared_secret(secret: String) -> [u8; 20] { } } +fn generate_confirmation_hash_for_time(time: i64, tag: &str, identity_secret: &String) -> String { + let decode: &[u8] = &base64::decode(&identity_secret).unwrap(); + let time_bytes = build_time_bytes(time); + let tag_bytes = tag.as_bytes(); + let array = [&time_bytes, tag_bytes].concat(); + let hash = hmac_sha1(decode, &array); + let encoded = base64::encode(hash); + return encoded; +} + impl SteamGuardAccount { pub fn new() -> Self { return SteamGuardAccount{ @@ -86,7 +94,7 @@ impl SteamGuardAccount { pub fn generate_code(&self, time: i64) -> String { let steam_guard_code_translations: [u8; 26] = [50, 51, 52, 53, 54, 55, 56, 57, 66, 67, 68, 70, 71, 72, 74, 75, 77, 78, 80, 81, 82, 84, 86, 87, 88, 89]; - let time_bytes: [u8; 8] = build_time_bytes(time); + let time_bytes: [u8; 8] = build_time_bytes(time / 30i64); let shared_secret: [u8; 20] = parse_shared_secret(self.shared_secret.clone()); // println!("time_bytes: {:?}", time_bytes); let hashed_data = hmacsha1::hmac_sha1(&shared_secret, &time_bytes); @@ -115,23 +123,13 @@ impl SteamGuardAccount { let mut params = HashMap::new(); params.insert("p", self.device_id.clone()); params.insert("a", session.steam_id.to_string()); - params.insert("k", self.generate_confirmation_hash_for_time(time, tag)); + params.insert("k", generate_confirmation_hash_for_time(time, tag, &self.identity_secret)); params.insert("t", time.to_string()); params.insert("m", String::from("android")); params.insert("tag", String::from(tag)); return params; } - fn generate_confirmation_hash_for_time(&self, time: i64, tag: &str) -> String { - let decode: &[u8] = &base64::decode(&self.identity_secret).unwrap(); - let time_bytes = build_time_bytes(time); - let tag_bytes = tag.as_bytes(); - let array = [&time_bytes, tag_bytes].concat(); - let hash = hmac_sha1(decode, &array); - let encoded = base64::encode(hash); - return encoded; - } - pub fn get_trade_confirmations(&self) { // uri: "https://steamcommunity.com/mobileconf/conf" // confirmation details: @@ -163,6 +161,14 @@ impl SteamGuardAccount { trace!("{:?}", resp); let text = resp.text().unwrap(); trace!("text: {:?}", text); + println!("{}", text); + // possible errors: + // + // Invalid authenticator: + //
Invalid authenticator
+ //
It looks like your Steam Guard Mobile Authenticator is providing incorrect Steam Guard codes. This could be caused by an inaccurate clock or bad timezone settings on your device. If your time settings are correct, it could be that a different device has been set up to provide the Steam Guard codes for your account, which means the authenticator on this device is no longer valid.
+ // + //
Nothing to confirm
match CONFIRMATION_REGEX.captures(text.as_str()) { Some(caps) => { let conf_id = &caps[1]; @@ -196,4 +202,9 @@ mod tests { let code = account.generate_code(1616374841i64); assert_eq!(code, "2F9J5") } + + #[test] + fn test_generate_confirmation_hash_for_time() { + assert_eq!(generate_confirmation_hash_for_time(1617591917, "conf", &String::from("GQP46b73Ws7gr8GmZFR0sDuau5c=")), String::from("NaL8EIMhfy/7vBounJ0CvpKbrPk=")); + } } From c936d9a0aca0ea63c169e3317bba3902291ce578 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 27 Jul 2021 10:10:20 -0400 Subject: [PATCH 25/36] add uuid crate --- Cargo.lock | 10 ++++++++++ Cargo.toml | 1 + 2 files changed, 11 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 8af5142..e9c42dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1301,6 +1301,7 @@ dependencies = [ "standback", "stderrlog", "text_io", + "uuid", ] [[package]] @@ -1609,6 +1610,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.2", +] + [[package]] name = "vcpkg" version = "0.2.11" diff --git a/Cargo.toml b/Cargo.toml index 3e2d098..e0466f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ stderrlog = "0.4" cookie = "0.14" regex = "1" lazy_static = "1.4.0" +uuid = { version = "0.8", features = ["v4"] } From d8ffc179e0c61b2ae9a9cb64e2306b605b56630f Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 27 Jul 2021 16:24:56 -0400 Subject: [PATCH 26/36] add incomplete account linker --- src/accountlinker.rs | 86 +++++++++++++++++++++++++++++++++++++++ src/main.rs | 96 ++++++++++++++++++++++++++++++-------------- 2 files changed, 152 insertions(+), 30 deletions(-) diff --git a/src/accountlinker.rs b/src/accountlinker.rs index e69de29..12faa1f 100644 --- a/src/accountlinker.rs +++ b/src/accountlinker.rs @@ -0,0 +1,86 @@ +use std::collections::HashMap; +use reqwest::{Url, cookie::{CookieStore}, header::COOKIE, header::{SET_COOKIE, USER_AGENT}}; +use serde::{Serialize, Deserialize}; +use serde_json::Value; +use steamguard_cli::{SteamGuardAccount, steamapi::Session}; +use log::*; + +#[derive(Debug, Clone)] +pub struct AccountLinker { + device_id: String, + phone_number: String, + pub account: SteamGuardAccount, + client: reqwest::blocking::Client, +} + +impl AccountLinker { + pub fn new() -> AccountLinker { + return AccountLinker{ + device_id: generate_device_id(), + phone_number: String::from(""), + account: SteamGuardAccount::new(), + client: reqwest::blocking::ClientBuilder::new() + .cookie_store(true) + .build() + .unwrap(), + } + } + + pub fn link(&self, session: &mut Session) { + let mut params = HashMap::new(); + params.insert("access_token", session.token.clone()); + params.insert("steamid", session.steam_id.to_string()); + params.insert("device_identifier", self.device_id.clone()); + params.insert("authenticator_type", String::from("1")); + params.insert("sms_phone_id", String::from("1")); + } + + fn has_phone(&self, session: &Session) -> bool { + return self._phoneajax(session, "has_phone", "null"); + } + + fn _phoneajax(&self, session: &Session, op: &str, arg: &str) -> bool { + trace!("_phoneajax: op={}", op); + let url = "https://steamcommunity.com".parse::().unwrap(); + let cookies = reqwest::cookie::Jar::default(); + cookies.add_cookie_str("mobileClientVersion=0 (2.1.3)", &url); + cookies.add_cookie_str("mobileClient=android", &url); + cookies.add_cookie_str("Steam_Language=english", &url); + + let mut params = HashMap::new(); + params.insert("op", op); + params.insert("arg", arg); + params.insert("sessionid", session.session_id.as_str()); + if op == "check_sms_code" { + params.insert("checkfortos", "0"); + params.insert("skipvoip", "1"); + } + + let resp = self.client + .post("https://steamcommunity.com/steamguard/phoneajax") + .header(COOKIE, cookies.cookies(&url).unwrap()) + .send() + .unwrap(); + + let result: Value = resp.json().unwrap(); + if result["has_phone"] != Value::Null { + trace!("found has_phone field"); + return result["has_phone"].as_bool().unwrap(); + } else if result["success"] != Value::Null { + trace!("found success field"); + return result["success"].as_bool().unwrap(); + } else { + trace!("did not find any expected field"); + return false; + } + } +} + +fn generate_device_id() -> String { + return format!("android:{}", uuid::Uuid::new_v4().to_string()); +} + +#[derive(Debug, Clone, Deserialize)] +pub struct AddAuthenticatorResponse { + pub response: SteamGuardAccount +} diff --git a/src/main.rs b/src/main.rs index 2f29419..e274943 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ extern crate rpassword; use borrow::BorrowMut; use io::Write; +use steamapi::Session; use steamguard_cli::*; use ::std::*; use text_io::read; @@ -12,6 +13,7 @@ use regex::Regex; #[macro_use] extern crate lazy_static; mod accountmanager; +mod accountlinker; lazy_static! { static ref CAPTCHA_VALID_CHARS: Regex = Regex::new("^([A-H]|[J-N]|[P-R]|[T-Z]|[2-4]|[7-9]|[@%&])+$").unwrap(); @@ -67,6 +69,10 @@ fn main() { .help("Accept all open trade confirmations. Does not open interactive interface.") ) ) + .subcommand( + App::new("setup") + .about("Set up a new account with steamguard-cli") + ) .get_matches(); @@ -88,6 +94,15 @@ fn main() { } manifest.load_accounts(); + + if matches.is_present("setup") { + info!("setup"); + let mut linker = accountlinker::AccountLinker::new(); + do_login(&mut linker.account); + // linker.link(linker.account.session.expect("no login session")); + return; + } + let mut selected_accounts: Vec = vec![]; if matches.is_present("all") { // manifest.accounts.iter().map(|a| selected_accounts.push(a.b)); @@ -111,36 +126,9 @@ fn main() { if matches.is_present("trade") { info!("trade"); - for account in selected_accounts.iter_mut() { - let _ = std::io::stdout().flush(); - let password = rpassword::prompt_password_stdout("Password: ").unwrap(); - trace!("password: {}", password); - let mut login = steamapi::UserLogin::new(account.account_name.clone(), password); - let mut loops = 0; - loop { - match login.login() { - steamapi::LoginResult::Ok(s) => { - account.session = Option::Some(s); - break; - } - steamapi::LoginResult::Need2FA => { - let server_time = steamapi::get_server_time(); - login.twofactor_code = account.generate_code(server_time); - } - steamapi::LoginResult::NeedCaptcha{ captcha_gid } => { - login.captcha_text = prompt_captcha_text(&captcha_gid); - } - r => { - error!("Fatal login result: {:?}", r); - return; - } - } - loops += 1; - if loops > 2 { - error!("Too many loops. Aborting login process, to avoid getting rate limited."); - return; - } - } + for a in selected_accounts.iter_mut() { + let mut account = a; // why is this necessary? + do_login(&mut account); info!("Checking for trade confirmations"); account.get_trade_confirmations(); @@ -198,3 +186,51 @@ fn prompt_captcha_text(captcha_gid: &String) -> String { } return captcha_text; } + +fn do_login(account: &mut SteamGuardAccount) { + if account.account_name.len() > 0 { + println!("Username: {}", account.account_name); + } else { + print!("Username: "); + account.account_name = prompt(); + } + let _ = std::io::stdout().flush(); + let password = rpassword::prompt_password_stdout("Password: ").unwrap(); + if password.len() > 0 { + debug!("password is present"); + } else { + debug!("password is empty"); + } + // TODO: reprompt if password is empty + let mut login = steamapi::UserLogin::new(account.account_name.clone(), password); + let mut loops = 0; + loop { + match login.login() { + steamapi::LoginResult::Ok(s) => { + account.session = Option::Some(s); + break; + } + steamapi::LoginResult::Need2FA => { + let server_time = steamapi::get_server_time(); + login.twofactor_code = account.generate_code(server_time); + } + steamapi::LoginResult::NeedCaptcha{ captcha_gid } => { + login.captcha_text = prompt_captcha_text(&captcha_gid); + } + steamapi::LoginResult::NeedEmail => { + println!("You should have received an email with a code."); + print!("Enter code"); + login.email_code = prompt(); + } + r => { + error!("Fatal login result: {:?}", r); + return; + } + } + loops += 1; + if loops > 2 { + error!("Too many loops. Aborting login process, to avoid getting rate limited."); + return; + } + } +} From d2bf5478ba2a6efb8806421d1222be8fc5d9ce71 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 27 Jul 2021 19:50:39 -0400 Subject: [PATCH 27/36] require login only when session is invalid --- src/lib.rs | 10 ++++++---- src/main.rs | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d5531c7..e9dd82e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,7 +130,7 @@ impl SteamGuardAccount { return params; } - pub fn get_trade_confirmations(&self) { + pub fn get_trade_confirmations(&self) -> Result, reqwest::Error> { // uri: "https://steamcommunity.com/mobileconf/conf" // confirmation details: let url = "https://steamcommunity.com".parse::().unwrap(); @@ -175,16 +175,18 @@ impl SteamGuardAccount { let conf_key = &caps[2]; let conf_type = &caps[3]; let conf_creator = &caps[4]; - debug!("{} {} {} {}", conf_id, conf_key, conf_type, conf_creator); + debug!("conf_id={} conf_key={} conf_type={} conf_creator={}", conf_id, conf_key, conf_type, conf_creator); + return Ok(vec![format!("conf_id={} conf_key={} conf_type={} conf_creator={}", conf_id, conf_key, conf_type, conf_creator)]); } _ => { info!("No confirmations"); + return Ok(vec![]); } - } + }; } Err(e) => { error!("error: {:?}", e); - return; + return Err(e); } } } diff --git a/src/main.rs b/src/main.rs index e274943..04a925a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -128,10 +128,22 @@ fn main() { info!("trade"); for a in selected_accounts.iter_mut() { let mut account = a; // why is this necessary? - do_login(&mut account); info!("Checking for trade confirmations"); - account.get_trade_confirmations(); + loop { + match account.get_trade_confirmations() { + Ok(confs) => { + for conf in confs { + println!("{}", conf); + } + break; + } + Err(_) => { + info!("failed to get trade confirmations, asking user to log in"); + do_login(&mut account); + } + } + } } } else { let server_time = steamapi::get_server_time(); From c23db2a5e917d94ee9ae99f7ac1a84911fcff574 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 27 Jul 2021 23:49:53 -0400 Subject: [PATCH 28/36] add Confirmation struct, and improve confirmation parsing --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/confirmation.rs | 33 +++++++++++++++++++++++++++++++++ src/lib.rs | 27 +++++++++++++++++++-------- src/main.rs | 2 +- 5 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 src/confirmation.rs diff --git a/Cargo.lock b/Cargo.lock index e9c42dc..25b0ac6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" + [[package]] name = "async-compression" version = "0.3.7" @@ -1285,6 +1291,7 @@ checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" name = "steamguard-cli" version = "0.2.0" dependencies = [ + "anyhow", "base64", "clap", "cookie", diff --git a/Cargo.toml b/Cargo.toml index e0466f9..ea115bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "^1.0" hmac-sha1 = "^0.1" base64 = "0.13.0" text_io = "0.1.8" diff --git a/src/confirmation.rs b/src/confirmation.rs new file mode 100644 index 0000000..b5570a1 --- /dev/null +++ b/src/confirmation.rs @@ -0,0 +1,33 @@ +/// A mobile confirmation. There are multiple things that can be confirmed, like trade offers. +#[derive(Debug, Clone)] +pub struct Confirmation { + pub id: u64, + pub key: u64, + /// Comes from the `data-type` attribute in the HTML + pub int_type: i32, + /// Trade offer ID or market transaction ID + pub creator: u64, + pub conf_type: ConfirmationType, + pub description: String, +} + +#[derive(Debug, Clone, Copy)] +pub enum ConfirmationType { + Generic = 1, + Trade = 2, + MarketSell = 3, + AccountRecovery = 6, + Unknown +} + +impl From<&str> for ConfirmationType { + fn from(text: &str) -> Self { + match text { + "1" => ConfirmationType::Generic, + "2" => ConfirmationType::Trade, + "3" => ConfirmationType::MarketSell, + "6" => ConfirmationType::AccountRecovery, + _ => ConfirmationType::Unknown, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index e9dd82e..68daf53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ use std::{collections::HashMap, convert::TryInto, thread, time}; +use confirmation::{Confirmation, ConfirmationType}; use hmacsha1::hmac_sha1; use regex::Regex; use reqwest::{Url, cookie::CookieStore, header::{COOKIE, USER_AGENT}}; @@ -6,8 +7,11 @@ use serde::{Serialize, Deserialize}; use log::*; #[macro_use] extern crate lazy_static; +#[macro_use] +extern crate anyhow; pub mod steamapi; +mod confirmation; // const STEAMAPI_BASE: String = "https://api.steampowered.com"; // const COMMUNITY_BASE: String = "https://steamcommunity.com"; @@ -130,7 +134,7 @@ impl SteamGuardAccount { return params; } - pub fn get_trade_confirmations(&self) -> Result, reqwest::Error> { + pub fn get_trade_confirmations(&self) -> Result, anyhow::Error> { // uri: "https://steamcommunity.com/mobileconf/conf" // confirmation details: let url = "https://steamcommunity.com".parse::().unwrap(); @@ -171,12 +175,19 @@ impl SteamGuardAccount { //
Nothing to confirm
match CONFIRMATION_REGEX.captures(text.as_str()) { Some(caps) => { - let conf_id = &caps[1]; - let conf_key = &caps[2]; - let conf_type = &caps[3]; - let conf_creator = &caps[4]; - debug!("conf_id={} conf_key={} conf_type={} conf_creator={}", conf_id, conf_key, conf_type, conf_creator); - return Ok(vec![format!("conf_id={} conf_key={} conf_type={} conf_creator={}", conf_id, conf_key, conf_type, conf_creator)]); + let conf_id = caps[1].parse()?; + let conf_key = caps[2].parse()?; + let conf_type = caps[3].try_into().unwrap_or(ConfirmationType::Unknown); + let conf_creator = caps[4].parse()?; + debug!("conf_id={} conf_key={} conf_type={:?} conf_creator={}", conf_id, conf_key, conf_type, conf_creator); + return Ok(vec![Confirmation { + id: conf_id, + key: conf_key, + conf_type: conf_type, + creator: conf_creator, + int_type: 0, + description: "".into(), + }]); } _ => { info!("No confirmations"); @@ -186,7 +197,7 @@ impl SteamGuardAccount { } Err(e) => { error!("error: {:?}", e); - return Err(e); + bail!(e); } } } diff --git a/src/main.rs b/src/main.rs index 04a925a..2ea030f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -134,7 +134,7 @@ fn main() { match account.get_trade_confirmations() { Ok(confs) => { for conf in confs { - println!("{}", conf); + println!("{:?}", conf); } break; } From 8b5d0ef0ff67c3c9f1de238ca05147c0f2c0be55 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Wed, 28 Jul 2021 14:10:14 -0400 Subject: [PATCH 29/36] move confirmation description to function probably temporary --- src/confirmation.rs | 10 ++++++++-- src/lib.rs | 1 - src/main.rs | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/confirmation.rs b/src/confirmation.rs index b5570a1..ddecc6a 100644 --- a/src/confirmation.rs +++ b/src/confirmation.rs @@ -1,5 +1,5 @@ /// A mobile confirmation. There are multiple things that can be confirmed, like trade offers. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Confirmation { pub id: u64, pub key: u64, @@ -8,7 +8,13 @@ pub struct Confirmation { /// Trade offer ID or market transaction ID pub creator: u64, pub conf_type: ConfirmationType, - pub description: String, +} + +impl Confirmation { + /// Human readable representation of this confirmation. + pub fn description(&self) -> String { + format!("{:?} id={} key={}", self.conf_type, self.id, self.key) + } } #[derive(Debug, Clone, Copy)] diff --git a/src/lib.rs b/src/lib.rs index 68daf53..b5956e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,7 +186,6 @@ impl SteamGuardAccount { conf_type: conf_type, creator: conf_creator, int_type: 0, - description: "".into(), }]); } _ => { diff --git a/src/main.rs b/src/main.rs index 2ea030f..abb7efe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -134,7 +134,7 @@ fn main() { match account.get_trade_confirmations() { Ok(confs) => { for conf in confs { - println!("{:?}", conf); + println!("{}", conf.description()); } break; } From 489842cc78fc6d7c7e1579cf1ee45b7e570b540c Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Wed, 28 Jul 2021 15:07:15 -0400 Subject: [PATCH 30/36] add send_confirmation_ajax --- src/lib.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b5956e7..955f40f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ use std::{collections::HashMap, convert::TryInto, thread, time}; +use anyhow::Result; use confirmation::{Confirmation, ConfirmationType}; use hmacsha1::hmac_sha1; use regex::Regex; @@ -134,9 +135,7 @@ impl SteamGuardAccount { return params; } - pub fn get_trade_confirmations(&self) -> Result, anyhow::Error> { - // uri: "https://steamcommunity.com/mobileconf/conf" - // confirmation details: + fn build_cookie_jar(&self) -> reqwest::cookie::Jar { let url = "https://steamcommunity.com".parse::().unwrap(); let cookies = reqwest::cookie::Jar::default(); let session = self.session.clone().unwrap(); @@ -149,10 +148,17 @@ impl SteamGuardAccount { cookies.add_cookie_str(format!("steamid={}", session.steam_id).as_str(), &url); cookies.add_cookie_str(format!("steamLogin={}", session.steam_login).as_str(), &url); cookies.add_cookie_str(format!("steamLoginSecure={}", session.steam_login_secure).as_str(), &url); + return cookies; + } + + pub fn get_trade_confirmations(&self) -> Result, anyhow::Error> { + // uri: "https://steamcommunity.com/mobileconf/conf" + // confirmation details: + let url = "https://steamcommunity.com".parse::().unwrap(); + let cookies = self.build_cookie_jar(); let client = reqwest::blocking::ClientBuilder::new() .cookie_store(true) - .build() - .unwrap(); + .build()?; match client .get("https://steamcommunity.com/mobileconf/conf".parse::().unwrap()) @@ -200,6 +206,41 @@ impl SteamGuardAccount { } } } + + /// Respond to a confirmation. + /// + /// Host: https://steamcommunity.com + /// Steam Endpoint: `POST /mobileconf/ajaxop` + fn send_confirmation_ajax(&self, conf: Confirmation, operation: String) -> anyhow::Result<()> { + ensure!(operation == "allow" || operation == "cancel"); + + let url = "https://steamcommunity.com".parse::().unwrap(); + let cookies = self.build_cookie_jar(); + let client = reqwest::blocking::ClientBuilder::new() + .cookie_store(true) + .build()?; + + let mut query_params = self.get_confirmation_query_params("conf"); + query_params.insert("op", operation); + query_params.insert("cid", conf.id.to_string()); + query_params.insert("ck", conf.key.to_string()); + + #[derive(Debug, Clone, Copy, Deserialize)] + struct SendConfirmationResponse { + pub success: bool + } + + let resp: SendConfirmationResponse = client.get("https://steamcommunity.com/mobileconf/ajaxop".parse::().unwrap()) + .header("X-Requested-With", "com.valvesoftware.android.steam.community") + .header(USER_AGENT, "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30") + .header(COOKIE, cookies.cookies(&url).unwrap()) + .query(&query_params) + .send()? + .json()?; + + ensure!(resp.success); + Ok(()) + } } #[cfg(test)] From d4c1758f1b5426c7ad3abe01c55a0f799ed80e1e Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Wed, 28 Jul 2021 15:48:06 -0400 Subject: [PATCH 31/36] implement --accept-all --- src/lib.rs | 10 +++++++++- src/main.rs | 11 +++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 955f40f..2bab7ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -211,7 +211,7 @@ impl SteamGuardAccount { /// /// Host: https://steamcommunity.com /// Steam Endpoint: `POST /mobileconf/ajaxop` - fn send_confirmation_ajax(&self, conf: Confirmation, operation: String) -> anyhow::Result<()> { + fn send_confirmation_ajax(&self, conf: &Confirmation, operation: String) -> anyhow::Result<()> { ensure!(operation == "allow" || operation == "cancel"); let url = "https://steamcommunity.com".parse::().unwrap(); @@ -241,6 +241,14 @@ impl SteamGuardAccount { ensure!(resp.success); Ok(()) } + + pub fn accept_confirmation(&self, conf: &Confirmation) -> anyhow::Result<()> { + self.send_confirmation_ajax(conf, "allow".into()) + } + + pub fn deny_confirmation(&self, conf: &Confirmation) -> anyhow::Result<()> { + self.send_confirmation_ajax(conf, "cancel".into()) + } } #[cfg(test)] diff --git a/src/main.rs b/src/main.rs index abb7efe..fb1129c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -124,7 +124,7 @@ fn main() { debug!("selected accounts: {:?}", selected_accounts.iter().map(|a| a.account_name.clone()).collect::>()); - if matches.is_present("trade") { + if let Some(trade_matches) = matches.subcommand_matches("trade") { info!("trade"); for a in selected_accounts.iter_mut() { let mut account = a; // why is this necessary? @@ -133,9 +133,16 @@ fn main() { loop { match account.get_trade_confirmations() { Ok(confs) => { - for conf in confs { + for conf in &confs { println!("{}", conf.description()); } + if trade_matches.is_present("accept-all") { + info!("accepting all confirmations"); + for conf in &confs { + let result = account.accept_confirmation(conf); + debug!("accept confirmation result: {:?}", result); + } + } break; } Err(_) => { From e3f7d10394efd612625421742794849fc5cfa2a9 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Wed, 28 Jul 2021 16:09:21 -0400 Subject: [PATCH 32/36] add get_confirmation_details --- src/lib.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2bab7ae..eed5a2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -210,7 +210,7 @@ impl SteamGuardAccount { /// Respond to a confirmation. /// /// Host: https://steamcommunity.com - /// Steam Endpoint: `POST /mobileconf/ajaxop` + /// Steam Endpoint: `GET /mobileconf/ajaxop` fn send_confirmation_ajax(&self, conf: &Confirmation, operation: String) -> anyhow::Result<()> { ensure!(operation == "allow" || operation == "cancel"); @@ -249,6 +249,34 @@ impl SteamGuardAccount { pub fn deny_confirmation(&self, conf: &Confirmation) -> anyhow::Result<()> { self.send_confirmation_ajax(conf, "cancel".into()) } + + /// Steam Endpoint: `GET /mobileconf/details/:id` + pub fn get_confirmation_details(&self, conf: &Confirmation) -> anyhow::Result { + #[derive(Debug, Clone, Deserialize)] + struct ConfirmationDetailsResponse { + pub success: bool, + pub html: String, + } + + let url = "https://steamcommunity.com".parse::().unwrap(); + let cookies = self.build_cookie_jar(); + let client = reqwest::blocking::ClientBuilder::new() + .cookie_store(true) + .build()?; + + let query_params = self.get_confirmation_query_params("details"); + + let resp: ConfirmationDetailsResponse = client.get(format!("https://steamcommunity.com/mobileconf/details/{}", conf.id).parse::().unwrap()) + .header("X-Requested-With", "com.valvesoftware.android.steam.community") + .header(USER_AGENT, "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30") + .header(COOKIE, cookies.cookies(&url).unwrap()) + .query(&query_params) + .send()? + .json()?; + + ensure!(resp.success); + Ok(resp.html) + } } #[cfg(test)] From bbc2f77b303359cd28a9a64e3cb77da9be4b86d3 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 29 Jul 2021 09:08:06 -0400 Subject: [PATCH 33/36] adjust trade subcommand loop --- src/lib.rs | 2 +- src/main.rs | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index eed5a2d..b51c5ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, convert::TryInto, thread, time}; use anyhow::Result; -use confirmation::{Confirmation, ConfirmationType}; +pub use confirmation::{Confirmation, ConfirmationType}; use hmacsha1::hmac_sha1; use regex::Regex; use reqwest::{Url, cookie::CookieStore, header::{COOKIE, USER_AGENT}}; diff --git a/src/main.rs b/src/main.rs index fb1129c..e3deff5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -130,19 +130,11 @@ fn main() { let mut account = a; // why is this necessary? info!("Checking for trade confirmations"); + let confirmations: Vec; loop { match account.get_trade_confirmations() { Ok(confs) => { - for conf in &confs { - println!("{}", conf.description()); - } - if trade_matches.is_present("accept-all") { - info!("accepting all confirmations"); - for conf in &confs { - let result = account.accept_confirmation(conf); - debug!("accept confirmation result: {:?}", result); - } - } + confirmations = confs; break; } Err(_) => { @@ -151,6 +143,20 @@ fn main() { } } } + + for conf in &confirmations { + println!("{}", conf.description()); + } + if trade_matches.is_present("accept-all") { + info!("accepting all confirmations"); + for conf in &confirmations { + let result = account.accept_confirmation(conf); + debug!("accept confirmation result: {:?}", result); + } + } + else { + todo!("check atty, show UI for accepting/denying confirmations"); + } } } else { let server_time = steamapi::get_server_time(); From 3dcccd1e63e5d481b23fb73911374be32648247d Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 29 Jul 2021 19:42:45 -0400 Subject: [PATCH 34/36] add interactive confirmation menu --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 157 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25b0ac6..5010eb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1307,6 +1307,7 @@ dependencies = [ "serde_json", "standback", "stderrlog", + "termion", "text_io", "uuid", ] diff --git a/Cargo.toml b/Cargo.toml index ea115bb..379f8c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,4 @@ cookie = "0.14" regex = "1" lazy_static = "1.4.0" uuid = { version = "0.8", features = ["v4"] } +termion = "1.5.6" diff --git a/src/main.rs b/src/main.rs index e3deff5..98bed8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,18 @@ extern crate rpassword; use borrow::BorrowMut; -use io::Write; +use collections::HashSet; +use io::{Write, stdout}; use steamapi::Session; use steamguard_cli::*; +use termion::{color::Color, raw::IntoRawMode, screen::AlternateScreen}; use ::std::*; use text_io::read; -use std::{io::stdin, path::Path}; +use std::{convert::TryInto, io::stdin, path::Path, sync::Arc}; use clap::{App, Arg, crate_version}; use log::*; use regex::Regex; +use termion::event::{Key, Event}; +use termion::input::{TermRead}; #[macro_use] extern crate lazy_static; @@ -73,6 +77,14 @@ fn main() { App::new("setup") .about("Set up a new account with steamguard-cli") ) + .subcommand( + App::new("debug") + .arg( + Arg::with_name("demo-conf-menu") + .help("Show an example confirmation menu using dummy data.") + .takes_value(false) + ) + ) .get_matches(); @@ -81,6 +93,13 @@ fn main() { .verbosity(verbosity) .module(module_path!()).init().unwrap(); + if let Some(demo_matches) = matches.subcommand_matches("debug") { + if demo_matches.is_present("demo-conf-menu") { + demo_confirmation_menu(); + } + return; + } + let path = Path::new(matches.value_of("mafiles-path").unwrap()).join("manifest.json"); let mut manifest: accountmanager::Manifest; match accountmanager::Manifest::load(path.as_path()) { @@ -144,9 +163,6 @@ fn main() { } } - for conf in &confirmations { - println!("{}", conf.description()); - } if trade_matches.is_present("accept-all") { info!("accepting all confirmations"); for conf in &confirmations { @@ -155,7 +171,23 @@ fn main() { } } else { - todo!("check atty, show UI for accepting/denying confirmations"); + if termion::is_tty(&stdout()) { + let (accept, deny) = prompt_confirmation_menu(confirmations); + for conf in &accept { + let result = account.accept_confirmation(conf); + debug!("accept confirmation result: {:?}", result); + } + for conf in &deny { + let result = account.deny_confirmation(conf); + debug!("deny confirmation result: {:?}", result); + } + } + else { + warn!("not a tty, not showing menu"); + for conf in &confirmations { + println!("{}", conf.description()); + } + } } } } else { @@ -212,6 +244,88 @@ fn prompt_captcha_text(captcha_gid: &String) -> String { return captcha_text; } +/// Returns a tuple of (accepted, denied). Ignored confirmations are not included. +fn prompt_confirmation_menu(confirmations: Vec) -> (Vec, Vec) { + println!("press a key other than enter to show the menu."); + let mut to_accept_idx: HashSet = HashSet::new(); + let mut to_deny_idx: HashSet = HashSet::new(); + + let mut screen = AlternateScreen::from(stdout().into_raw_mode().unwrap()); + let stdin = stdin(); + + let mut selected_idx = 0; + + for c in stdin.events() { + match c.expect("could not get events") { + Event::Key(Key::Char('a')) => { + to_accept_idx.insert(selected_idx); + to_deny_idx.remove(&selected_idx); + } + Event::Key(Key::Char('d')) => { + to_accept_idx.remove(&selected_idx); + to_deny_idx.insert(selected_idx); + } + Event::Key(Key::Char('i')) => { + to_accept_idx.remove(&selected_idx); + to_deny_idx.remove(&selected_idx); + } + Event::Key(Key::Char('A')) => { + (0..confirmations.len()).for_each(|i| { to_accept_idx.insert(i); to_deny_idx.remove(&i); }); + } + Event::Key(Key::Char('D')) => { + (0..confirmations.len()).for_each(|i| { to_accept_idx.remove(&i); to_deny_idx.insert(i); }); + } + Event::Key(Key::Char('I')) => { + (0..confirmations.len()).for_each(|i| { to_accept_idx.remove(&i); to_deny_idx.remove(&i); }); + } + Event::Key(Key::Up) if selected_idx > 0 => { + selected_idx -= 1; + } + Event::Key(Key::Down) if selected_idx < confirmations.len() - 1 => { + selected_idx += 1; + } + Event::Key(Key::Char('\n')) => { + break; + } + Event::Key(Key::Esc) | Event::Key(Key::Ctrl('c')) => { + return (vec![], vec![]); + } + _ => {} + } + + write!(screen, "{}{}{}arrow keys to select, [a]ccept, [d]eny, [i]gnore, [enter] confirm choices\n\n", termion::clear::All, termion::cursor::Goto(1, 1), termion::color::Fg(termion::color::White)).unwrap(); + for i in 0..confirmations.len() { + if selected_idx == i { + write!(screen, "\r{} >", termion::color::Fg(termion::color::LightYellow)).unwrap(); + } + else { + write!(screen, "\r{} ", termion::color::Fg(termion::color::White)).unwrap(); + } + + if to_accept_idx.contains(&i) { + write!(screen, "{}[a]", termion::color::Fg(termion::color::LightGreen)).unwrap(); + } + else if to_deny_idx.contains(&i) { + write!(screen, "{}[d]", termion::color::Fg(termion::color::LightRed)).unwrap(); + } + else { + write!(screen, "[ ]").unwrap(); + } + + if selected_idx == i { + write!(screen, "{}", termion::color::Fg(termion::color::LightYellow)).unwrap(); + } + + write!(screen, " {}\n", confirmations[i].description()).unwrap(); + } + } + + return ( + to_accept_idx.iter().map(|i| confirmations[*i]).collect(), + to_deny_idx.iter().map(|i| confirmations[*i]).collect(), + ); +} + fn do_login(account: &mut SteamGuardAccount) { if account.account_name.len() > 0 { println!("Username: {}", account.account_name); @@ -259,3 +373,38 @@ fn do_login(account: &mut SteamGuardAccount) { } } } + +fn demo_confirmation_menu() { + info!("showing demo menu"); + let (accept, deny) = prompt_confirmation_menu(vec![ + Confirmation { + id: 1234, + key: 12345, + conf_type: ConfirmationType::Trade, + int_type: 0, + creator: 09870987, + }, + Confirmation { + id: 1234, + key: 12345, + conf_type: ConfirmationType::MarketSell, + int_type: 0, + creator: 09870987, + }, + Confirmation { + id: 1234, + key: 12345, + conf_type: ConfirmationType::AccountRecovery, + int_type: 0, + creator: 09870987, + }, + Confirmation { + id: 1234, + key: 12345, + conf_type: ConfirmationType::Trade, + int_type: 0, + creator: 09870987, + }, + ]); + println!("accept: {}, deny: {}", accept.len(), deny.len()); +} From 4be38f8854ddfa8e9e29aedd5712ecf7a50f0682 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 29 Jul 2021 22:52:48 -0400 Subject: [PATCH 35/36] update to rsa 0.5 --- Cargo.lock | 254 +++++++++++++++++------------------------------- Cargo.toml | 4 +- src/steamapi.rs | 4 +- 3 files changed, 95 insertions(+), 167 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5010eb2..80a1ce1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,21 +78,18 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d27fb6b6f1e43147af148af49d49329413ba781aa0d5e10979831c210173b5" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "bumpalo" version = "3.6.1" @@ -151,6 +148,12 @@ dependencies = [ "vec_map", ] +[[package]] +name = "const-oid" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c32f031ea41b4291d695026c023b95d59db2d8a2c7640800ed56bc8f510f22" + [[package]] name = "const_fn" version = "0.4.5" @@ -200,12 +203,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" -[[package]] -name = "cpuid-bool" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" - [[package]] name = "crc32fast" version = "1.2.1" @@ -215,6 +212,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-bigint" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32a398eb1ccfbe7e4f452bc749c44d38dd732e9a253f19da224c416f00ee7f4" +dependencies = [ + "generic-array", + "rand_core", + "subtle", +] + +[[package]] +name = "der" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f215f706081a44cb702c71c39a52c05da637822e9c1645a50b7202689e982d" +dependencies = [ + "const-oid", + "crypto-bigint", +] + [[package]] name = "digest" version = "0.9.0" @@ -340,17 +358,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.2" @@ -359,7 +366,7 @@ checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" dependencies = [ "cfg-if", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -632,22 +639,11 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg 1.0.1", - "num-integer", - "num-traits", -] - [[package]] name = "num-bigint-dig" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d51546d704f52ef14b3c962b5776e53d5b862e5790e40a350d366c209bd7f7a" +checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480" dependencies = [ "autocfg 0.1.7", "byteorder", @@ -656,8 +652,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.7.3", - "serde", + "rand", "smallvec", "zeroize", ] @@ -690,6 +685,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg 1.0.1", + "libm", ] [[package]] @@ -714,12 +710,6 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "openssl" version = "0.10.33" @@ -754,14 +744,12 @@ dependencies = [ ] [[package]] -name = "pem" -version = "0.8.3" +name = "pem-rfc7468" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +checksum = "b8fe90c78c9a17442665a41a1a45dcd24bbab0e1794748edc19b27fffb146c13" dependencies = [ - "base64", - "once_cell", - "regex", + "base64ct", ] [[package]] @@ -802,6 +790,30 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "359e7852310174a810f078124edb73c66e88a1a731b2fd586dba34ee32dbe416" +dependencies = [ + "der", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee84ed13e44dd82689fa18348a49934fa79cc774a344c42fc9b301c71b140a" +dependencies = [ + "der", + "pem-rfc7468", + "pkcs1", + "spki", + "zeroize", +] + [[package]] name = "pkg-config" version = "0.3.19" @@ -850,37 +862,14 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", -] - -[[package]] -name = "rand" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ "libc", - "rand_chacha 0.3.0", - "rand_core 0.6.2", - "rand_hc 0.3.0", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", + "rand_hc", ] [[package]] @@ -890,16 +879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", - "rand_core 0.6.2", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -908,16 +888,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ - "getrandom 0.2.2", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -926,7 +897,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ - "rand_core 0.6.2", + "rand_core", ] [[package]] @@ -1025,9 +996,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3648b669b10afeab18972c105e284a7b953a669b0be3514c27f9b17acab2f9cd" +checksum = "e05c2603e2823634ab331437001b411b9ed11660fbc4066f3908c84a9439260d" dependencies = [ "byteorder", "digest", @@ -1036,12 +1007,10 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "pem", - "rand 0.7.3", - "sha2", - "simple_asn1", + "pkcs1", + "pkcs8", + "rand", "subtle", - "thiserror", "zeroize", ] @@ -1163,30 +1132,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -[[package]] -name = "sha2" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" -dependencies = [ - "block-buffer", - "cfg-if", - "cpuid-bool", - "digest", - "opaque-debug", -] - -[[package]] -name = "simple_asn1" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" -dependencies = [ - "chrono", - "num-bigint", - "num-traits", -] - [[package]] name = "slab" version = "0.4.2" @@ -1216,6 +1161,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spki" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "987637c5ae6b3121aba9d513f869bd2bff11c4cc086c22473befd6649c0bd521" +dependencies = [ + "der", +] + [[package]] name = "standback" version = "0.2.17" @@ -1298,7 +1252,7 @@ dependencies = [ "hmac-sha1", "lazy_static 1.4.0", "log", - "rand 0.7.3", + "rand", "regex", "reqwest", "rpassword", @@ -1355,7 +1309,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ "cfg-if", "libc", - "rand 0.8.3", + "rand", "redox_syscall", "remove_dir_all", "winapi", @@ -1397,26 +1351,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "thiserror" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "thread_local" version = "0.3.4" @@ -1624,7 +1558,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.2", + "getrandom", ] [[package]] @@ -1661,12 +1595,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 379f8c9..88bdf7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,8 @@ rpassword = "5.0" reqwest = { version = "0.11", features = ["blocking", "json", "cookies", "gzip"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -rsa = "0.3" -rand = "0.7.3" # rsa is not compatible with rand 0.8: https://github.com/RustCrypto/RSA/issues/81 +rsa = "0.5.0" +rand = "0.8.4" standback = "0.2.17" # required to fix a compilation error on a transient dependency clap = "2.33.3" log = "0.4.14" diff --git a/src/steamapi.rs b/src/steamapi.rs index 77da21d..a594013 100644 --- a/src/steamapi.rs +++ b/src/steamapi.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use reqwest::{Url, cookie::{CookieStore}, header::COOKIE, header::{SET_COOKIE, USER_AGENT}}; -use rsa::{PublicKey, RSAPublicKey}; +use rsa::{PublicKey, RsaPublicKey}; use std::time::{SystemTime, UNIX_EPOCH}; use serde::{Serialize, Deserialize}; use serde::de::{Visitor}; @@ -342,7 +342,7 @@ pub fn get_server_time() -> i64 { fn encrypt_password(rsa_resp: RsaResponse, password: &String) -> String { let rsa_exponent = rsa::BigUint::parse_bytes(rsa_resp.publickey_exp.as_bytes(), 16).unwrap(); let rsa_modulus = rsa::BigUint::parse_bytes(rsa_resp.publickey_mod.as_bytes(), 16).unwrap(); - let public_key = RSAPublicKey::new(rsa_modulus, rsa_exponent).unwrap(); + let public_key = RsaPublicKey::new(rsa_modulus, rsa_exponent).unwrap(); let mut rng = OsRng; let padding = rsa::PaddingScheme::new_pkcs1v15_encrypt(); let encrypted_password = base64::encode(public_key.encrypt(&mut rng, padding, password.as_bytes()).unwrap()); From 5e7b614e92b34ba238d40953f68d8d605ff7b684 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 29 Jul 2021 23:02:27 -0400 Subject: [PATCH 36/36] update readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e0f630c..a502198 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A linux utility for setting up and using Steam Mobile Authenticator (AKA Steam 2FA) on the command line. **This utility is in beta.** +**We are in the process of rewriting steamguard-cli from scratch in Rust.** Any help would be greatly appreciated! See #55 for discussion. The instructions in this document refer to the C# version. + # Disclaimer **Use this software at your own risk.** @@ -64,7 +66,7 @@ Coming soon... To install the latest version on Debian-based systems, download the package from the releases section and type sudo dpkg --install steamguard-cli_x.x.x.x-x.deb - + For Archlinux, install [steamguard-cli-git](https://aur.archlinux.org/packages/steamguard-cli-git/) from the AUR. To install after building from source, run: