From 5b8d581a48539eb9bd1965d68df513932ed13a82 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sun, 21 Mar 2021 21:21:29 -0400 Subject: [PATCH] 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) +}